Author: jh
Date: Thu Oct  7 18:00:55 2010
New Revision: 213526
URL: http://svn.freebsd.org/changeset/base/213526

Log:
  Check the device name validity on device registration.
  
  A new function prep_devname() sanitizes a device name by removing
  leading and redundant sequential slashes. The function returns an error
  for names which already exist or are considered invalid.
  
  A new flag MAKEDEV_CHECKNAME for make_dev_p(9) and make_dev_credf(9)
  indicates that the caller is prepared to handle an error related to the
  device name. An invalid name triggers a panic if the flag is not
  specified.
  
  Document the MAKEDEV_CHECKNAME flag in the make_dev(9) manual page.
  
  Idea from:    kib
  Reviewed by:  kib

Modified:
  head/share/man/man9/make_dev.9
  head/sys/kern/kern_conf.c
  head/sys/sys/conf.h

Modified: head/share/man/man9/make_dev.9
==============================================================================
--- head/share/man/man9/make_dev.9      Thu Oct  7 17:49:19 2010        
(r213525)
+++ head/share/man/man9/make_dev.9      Thu Oct  7 18:00:55 2010        
(r213526)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd September 5, 2010
+.Dd October 7, 2010
 .Dt MAKE_DEV 9
 .Os
 .Sh NAME
@@ -131,12 +131,18 @@ argument alters the operation of
 .Fn make_dev_credf .
 The following values are currently accepted:
 .Pp
-.Bd -literal -offset indent -compact
-MAKEDEV_REF    reference the created device
-MAKEDEV_NOWAIT do not sleep, may return NULL
-MAKEDEV_WAITOK allow the function to sleep to satisfy malloc
-MAKEDEV_ETERNAL        created device will be never destroyed
-.Ed
+.Bl -tag -width "MAKEDEV_CHECKNAME" -compact -offset indent
+.It MAKEDEV_REF
+reference the created device
+.It MAKEDEV_NOWAIT
+do not sleep, may return NULL
+.It MAKEDEV_WAITOK
+allow the function to sleep to satisfy malloc
+.It MAKEDEV_ETERNAL
+created device will be never destroyed
+.It MAKEDEV_CHECKNAME
+return NULL if the device name is invalid or already exists
+.El
 .Pp
 The
 .Dv MAKEDEV_WAITOK
@@ -166,6 +172,9 @@ For the convenience, use the
 flag for the code that can be compiled into kernel or loaded
 (and unloaded) as loadable module.
 .Pp
+A panic will occur if the MAKEDEV_CHECKNAME flag is not specified
+and the device name is invalid or already exists.
+.Pp
 The
 .Fn make_dev_cred
 function is equivalent to the call

Modified: head/sys/kern/kern_conf.c
==============================================================================
--- head/sys/kern/kern_conf.c   Thu Oct  7 17:49:19 2010        (r213525)
+++ head/sys/kern/kern_conf.c   Thu Oct  7 18:00:55 2010        (r213526)
@@ -682,26 +682,91 @@ prep_cdevsw(struct cdevsw *devsw, int fl
 }
 
 static int
+prep_devname(struct cdev *dev, const char *fmt, va_list ap)
+{
+       int len;
+       char *from, *q, *s, *to;
+
+       mtx_assert(&devmtx, MA_OWNED);
+
+       len = vsnrprintf(dev->__si_namebuf, sizeof(dev->__si_namebuf), 32,
+           fmt, ap);
+       if (len > sizeof(dev->__si_namebuf) - 1)
+               return (ENAMETOOLONG);
+
+       /* Strip leading slashes. */
+       for (from = dev->__si_namebuf; *from == '/'; from++)
+               ;
+
+       for (to = dev->__si_namebuf; *from != '\0'; from++, to++) {
+               /* Treat multiple sequential slashes as single. */
+               while (from[0] == '/' && from[1] == '/')
+                       from++;
+               /* Trailing slash is considered invalid. */
+               if (from[0] == '/' && from[1] == '\0')
+                       return (EINVAL);
+               *to = *from;
+       }
+       *to = '\0';
+
+       if (dev->__si_namebuf[0] == '\0')
+               return (EINVAL);
+
+       /* Disallow "." and ".." components. */
+       for (s = dev->__si_namebuf;;) {
+               for (q = s; *q != '/' && *q != '\0'; q++)
+                       ;
+               if (q - s == 1 && s[0] == '.')
+                       return (EINVAL);
+               if (q - s == 2 && s[0] == '.' && s[1] == '.')
+                       return (EINVAL);
+               if (*q != '/')
+                       break;
+               s = q + 1;
+       }
+
+       if (devfs_dev_exists(dev->__si_namebuf) != 0)
+               return (EEXIST);
+
+       return (0);
+}
+
+static int
 make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit,
     struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
     va_list ap)
 {
-       struct cdev *dev;
-       int i, res;
+       struct cdev *dev, *dev_new;
+       int res;
 
        KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0,
            ("make_dev_credv: both WAITOK and NOWAIT specified"));
-       dev = devfs_alloc(flags);
-       if (dev == NULL)
+       dev_new = devfs_alloc(flags);
+       if (dev_new == NULL)
                return (ENOMEM);
        dev_lock();
        res = prep_cdevsw(devsw, flags);
        if (res != 0) {
                dev_unlock();
-               devfs_free(dev);
+               devfs_free(dev_new);
                return (res);
        }
-       dev = newdev(devsw, unit, dev);
+       dev = newdev(devsw, unit, dev_new);
+       if ((dev->si_flags & SI_NAMED) == 0)
+               res = prep_devname(dev, fmt, ap);
+               if (res != 0) {
+                       if ((flags & MAKEDEV_CHECKNAME) == 0) {
+                               panic(
+                       "make_dev_credv: bad si_name (error=%d, si_name=%s)",
+                                   res, dev->si_name);
+                       }
+                       if (dev == dev_new) {
+                               LIST_REMOVE(dev, si_list);
+                               dev_unlock();
+                               devfs_free(dev);
+                       }
+                       return (res);
+       }
        if (flags & MAKEDEV_REF)
                dev_refl(dev);
        if (flags & MAKEDEV_ETERNAL)
@@ -720,13 +785,6 @@ make_dev_credv(int flags, struct cdev **
        KASSERT(!(dev->si_flags & SI_NAMED),
            ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
            devsw->d_name, dev2unit(dev), devtoname(dev)));
-
-       i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, 
ap);
-       if (i > (sizeof dev->__si_namebuf - 1)) {
-               printf("WARNING: Device name truncated! (%s)\n", 
-                   dev->__si_namebuf);
-       }
-               
        dev->si_flags |= SI_NAMED;
        if (cr != NULL)
                dev->si_cred = crhold(cr);
@@ -756,7 +814,8 @@ make_dev(struct cdevsw *devsw, int unit,
        res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt,
            ap);
        va_end(ap);
-       KASSERT(res == 0 && dev != NULL, ("make_dev: failed make_dev_credv"));
+       KASSERT(res == 0 && dev != NULL,
+           ("make_dev: failed make_dev_credv (error=%d)", res));
        return (dev);
 }
 
@@ -773,7 +832,7 @@ make_dev_cred(struct cdevsw *devsw, int 
        va_end(ap);
 
        KASSERT(res == 0 && dev != NULL,
-           ("make_dev_cred: failed make_dev_credv"));
+           ("make_dev_cred: failed make_dev_credv (error=%d)", res));
        return (dev);
 }
 
@@ -790,8 +849,9 @@ make_dev_credf(int flags, struct cdevsw 
            fmt, ap);
        va_end(ap);
 
-       KASSERT((flags & MAKEDEV_NOWAIT) != 0 || res == 0,
-           ("make_dev_credf: failed make_dev_credv"));
+       KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) ||
+           ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0,
+           ("make_dev_credf: failed make_dev_credv (error=%d)", res));
        return (res == 0 ? dev : NULL);
 }
 
@@ -807,8 +867,9 @@ make_dev_p(int flags, struct cdev **cdev
            fmt, ap);
        va_end(ap);
 
-       KASSERT((flags & MAKEDEV_NOWAIT) != 0 || res == 0,
-           ("make_dev_p: failed make_dev_credv"));
+       KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) ||
+           ((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0,
+           ("make_dev_p: failed make_dev_credv (error=%d)", res));
        return (res);
 }
 
@@ -836,21 +897,20 @@ make_dev_alias(struct cdev *pdev, const 
 {
        struct cdev *dev;
        va_list ap;
-       int i;
+       int error;
 
        KASSERT(pdev != NULL, ("NULL pdev"));
        dev = devfs_alloc(MAKEDEV_WAITOK);
        dev_lock();
        dev->si_flags |= SI_ALIAS;
-       dev->si_flags |= SI_NAMED;
        va_start(ap, fmt);
-       i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, 
ap);
-       if (i > (sizeof dev->__si_namebuf - 1)) {
-               printf("WARNING: Device name truncated! (%s)\n", 
-                   dev->__si_namebuf);
-       }
+       error = prep_devname(dev, fmt, ap);
        va_end(ap);
-
+       if (error != 0) {
+               panic("make_dev_alias: bad si_name (error=%d, si_name=%s)",
+                   error, dev->si_name);
+       }
+       dev->si_flags |= SI_NAMED;
        devfs_create(dev);
        dev_dependsl(pdev, dev);
        clean_unrhdrl(devfs_inos);

Modified: head/sys/sys/conf.h
==============================================================================
--- head/sys/sys/conf.h Thu Oct  7 17:49:19 2010        (r213525)
+++ head/sys/sys/conf.h Thu Oct  7 18:00:55 2010        (r213526)
@@ -263,11 +263,12 @@ struct cdev *make_dev(struct cdevsw *_de
 struct cdev *make_dev_cred(struct cdevsw *_devsw, int _unit,
                struct ucred *_cr, uid_t _uid, gid_t _gid, int _perms,
                const char *_fmt, ...) __printflike(7, 8);
-#define        MAKEDEV_REF     0x01
-#define        MAKEDEV_WHTOUT  0x02
-#define        MAKEDEV_NOWAIT  0x04
-#define        MAKEDEV_WAITOK  0x08
-#define        MAKEDEV_ETERNAL 0x10
+#define        MAKEDEV_REF             0x01
+#define        MAKEDEV_WHTOUT          0x02
+#define        MAKEDEV_NOWAIT          0x04
+#define        MAKEDEV_WAITOK          0x08
+#define        MAKEDEV_ETERNAL         0x10
+#define        MAKEDEV_CHECKNAME       0x20
 struct cdev *make_dev_credf(int _flags,
                struct cdevsw *_devsw, int _unit,
                struct ucred *_cr, uid_t _uid, gid_t _gid, int _mode,
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to