Make the data returned by devinfo harder to overflow.

Rather than using fixed-length strings, pack them into a string table
to return. Also expand the buffer from ~300 charaters to 3k. This should
be enough, even for USB.

This fixes a problem where USB pnp info is truncated on return to
userland.

Differential Revision: https://reviews.freebsd.org/D15629
This commit is contained in:
Warner Losh 2018-05-31 02:57:58 +00:00
parent 92376fa76c
commit c580ca4cf4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=334414
2 changed files with 51 additions and 26 deletions

View file

@ -5264,8 +5264,9 @@ sysctl_devices(SYSCTL_HANDLER_ARGS)
u_int namelen = arg2; u_int namelen = arg2;
int index; int index;
device_t dev; device_t dev;
struct u_device udev; /* XXX this is a bit big */ struct u_device *udev;
int error; int error;
char *walker, *ep;
if (namelen != 2) if (namelen != 2)
return (EINVAL); return (EINVAL);
@ -5286,24 +5287,45 @@ sysctl_devices(SYSCTL_HANDLER_ARGS)
return (ENOENT); return (ENOENT);
/* /*
* Populate the return array. * Populate the return item, careful not to overflow the buffer.
*/ */
bzero(&udev, sizeof(udev)); udev = malloc(sizeof(*udev), M_BUS, M_WAITOK | M_ZERO);
udev.dv_handle = (uintptr_t)dev; if (udev == NULL)
udev.dv_parent = (uintptr_t)dev->parent; return (ENOMEM);
if (dev->nameunit != NULL) udev->dv_handle = (uintptr_t)dev;
strlcpy(udev.dv_name, dev->nameunit, sizeof(udev.dv_name)); udev->dv_parent = (uintptr_t)dev->parent;
if (dev->desc != NULL) udev->dv_devflags = dev->devflags;
strlcpy(udev.dv_desc, dev->desc, sizeof(udev.dv_desc)); udev->dv_flags = dev->flags;
if (dev->driver != NULL && dev->driver->name != NULL) udev->dv_state = dev->state;
strlcpy(udev.dv_drivername, dev->driver->name, walker = udev->dv_fields;
sizeof(udev.dv_drivername)); ep = walker + sizeof(udev->dv_fields);
bus_child_pnpinfo_str(dev, udev.dv_pnpinfo, sizeof(udev.dv_pnpinfo)); #define CP(src) \
bus_child_location_str(dev, udev.dv_location, sizeof(udev.dv_location)); if ((src) == NULL) \
udev.dv_devflags = dev->devflags; *walker++ = '\0'; \
udev.dv_flags = dev->flags; else { \
udev.dv_state = dev->state; strlcpy(walker, (src), ep - walker); \
error = SYSCTL_OUT(req, &udev, sizeof(udev)); walker += strlen(walker) + 1; \
} \
if (walker >= ep) \
break;
do {
CP(dev->nameunit);
CP(dev->desc);
CP(dev->driver != NULL ? dev->driver->name : NULL);
bus_child_pnpinfo_str(dev, walker, ep - walker);
walker += strlen(walker) + 1;
if (walker >= ep)
break;
bus_child_location_str(dev, walker, ep - walker);
walker += strlen(walker) + 1;
if (walker >= ep)
break;
*walker++ = '\0';
} while (0);
#undef CP
error = SYSCTL_OUT(req, udev, sizeof(*udev));
free(udev, M_BUS);
return (error); return (error);
} }

View file

@ -46,7 +46,7 @@
*/ */
struct u_businfo { struct u_businfo {
int ub_version; /**< @brief interface version */ int ub_version; /**< @brief interface version */
#define BUS_USER_VERSION 1 #define BUS_USER_VERSION 2
int ub_generation; /**< @brief generation count */ int ub_generation; /**< @brief generation count */
}; };
@ -63,20 +63,23 @@ typedef enum device_state {
/** /**
* @brief Device information exported to userspace. * @brief Device information exported to userspace.
* The strings are placed one after the other, separated by NUL characters.
* Fields should be added after the last one and order maintained for compatibility
*/ */
#define BUS_USER_BUFFER (3*1024)
struct u_device { struct u_device {
uintptr_t dv_handle; uintptr_t dv_handle;
uintptr_t dv_parent; uintptr_t dv_parent;
char dv_name[32]; /**< @brief Name of device in tree. */
char dv_desc[32]; /**< @brief Driver description */
char dv_drivername[32]; /**< @brief Driver name */
char dv_pnpinfo[128]; /**< @brief Plug and play info */
char dv_location[128]; /**< @brief Where is the device? */
uint32_t dv_devflags; /**< @brief API Flags for device */ uint32_t dv_devflags; /**< @brief API Flags for device */
uint16_t dv_flags; /**< @brief flags for dev state */ uint16_t dv_flags; /**< @brief flags for dev state */
device_state_t dv_state; /**< @brief State of attachment */ device_state_t dv_state; /**< @brief State of attachment */
/* XXX more driver info? */ char dv_fields[BUS_USER_BUFFER]; /**< @brief NUL terminated fields */
/* name (name of the device in tree) */
/* desc (driver description) */
/* drivername (Name of driver without unit number) */
/* pnpinfo (Plug and play information from bus) */
/* location (Location of device on parent */
/* NUL */
}; };
/* Flags exported via dv_flags. */ /* Flags exported via dv_flags. */