kern/148546: [ipmi] Buffer overrun in the impi driver while processing smbios date

John Baldwin jhb at freebsd.org
Tue Jul 13 19:50:03 UTC 2010


The following reply was made to PR kern/148546; it has been noted by GNATS.

From: John Baldwin <jhb at freebsd.org>
To: bug-followup at freebsd.org
Cc: spencer_minear at mcafee.com
Subject: Re: kern/148546: [ipmi] Buffer overrun in the impi driver while processing smbios date
Date: Tue, 13 Jul 2010 15:48:10 -0400

 On Tuesday, July 13, 2010 3:06:02 pm John Baldwin wrote:
 > Hmm, the smbios table parser in ipmi_smbios.c is a bit broken. :(  I 
 > think it was derived from a more generic parser.  At some point it might 
 > be useful to write a more generic smbios table parser that this code 
 > could use, but the simplest fix might be to just simplify this code to 
 > be more IPMI specific.  For example, the IPMI table entry doesn't use 
 > the strings at all, so the table of strings could just be dropped.  We 
 > could also remove the dispatch table and instead check the table entry 
 > type in the the smbios_t38_proc_info() function.  This is more like what 
 > other places in the kernel do when walking tables e.g. the MADT or MP Table.
 
 Here is a possible implementation of this.  I have compiled it but have not 
 yet run tested it:
 
 Index: ipmi_smbios.c
 ===================================================================
 --- ipmi_smbios.c	(revision 209948)
 +++ ipmi_smbios.c	(working copy)
 @@ -52,7 +52,7 @@
  #define	pmap_unmapbios		pmap_unmapdev
  #endif
  
 -struct smbios_table_entry {
 +struct smbios_table {
  	uint8_t		anchor_string[4];
  	uint8_t		checksum;
  	uint8_t		length;
 @@ -108,27 +108,30 @@
  #define	SMBIOS_LEN		4
  #define	SMBIOS_SIG		"_SM_"
  
 -typedef void (*dispatchproc_t)(uint8_t *p, char **table,
 -    struct ipmi_get_info *info);
 +typedef void (*smbios_callback_t)(struct structure_header *, void *);
  
  static struct ipmi_get_info ipmi_info;
  static int ipmi_probed;
  static struct mtx ipmi_info_mtx;
  MTX_SYSINIT(ipmi_info, &ipmi_info_mtx, "ipmi info", MTX_DEF);
  
 -static char	*get_strings(char *, char **);
  static void	ipmi_smbios_probe(struct ipmi_get_info *);
 -static int	smbios_cksum	(struct smbios_table_entry *);
 -static void	smbios_run_table(uint8_t *, int, dispatchproc_t *,
 -		    struct ipmi_get_info *);
 -static void	smbios_t38_proc_info(uint8_t *, char **,
 -		    struct ipmi_get_info *);
 +static int	smbios_cksum(struct smbios_table *);
 +static void	smbios_walk_table(uint8_t *, int, smbios_callback_t,
 +		    void *);
 +static void	smbios_ipmi_info(struct structure_header *, void *);
  
  static void
 -smbios_t38_proc_info(uint8_t *p, char **table, struct ipmi_get_info *info)
 +smbios_ipmi_info(struct structure_header *h, void *arg)
  {
 -	struct ipmi_entry *s = (struct ipmi_entry *)p;
 +	struct ipmi_get_info *info;
 +	struct ipmi_entry *s;
  
 +	if (h->type != 38 || h->length <
 +	    offsetof(struct ipmi_entry, interrupt_number))
 +		return;
 +	s = (struct ipmi_entry *)h;
 +	info = arg;
  	bzero(info, sizeof(struct ipmi_get_info));
  	switch (s->interface_type) {
  	case KCS_MODE:
 @@ -172,44 +175,28 @@
  	info->iface_type = s->interface_type;
  }
  
 -static char *
 -get_strings(char *p, char **table)
 -{
 -	/* Scan for strings, stoping at a single null byte */
 -	while (*p != 0) {
 -		*table++ = p;
 -		p += strlen(p) + 1;
 -	}
 -	*table = 0;
 -
 -	/* Skip past terminating null byte */
 -	return (p + 1);
 -}
 -
 -
  static void
 -smbios_run_table(uint8_t *p, int entries, dispatchproc_t *dispatchstatus,
 -    struct ipmi_get_info *info)
 +smbios_walk_table(uint8_t *p, int entries, smbios_callback_t cb, void *arg)
  {
  	struct structure_header *s;
 -	char *table[20];
 -	uint8_t *nextp;
  
 -	while(entries--) {
 -		s = (struct structure_header *) p;
 -		nextp = get_strings(p + s->length, table);
 +	while (entries--) {
 +		s = (struct structure_header *)p;
 +		cb(s, arg);
  
  		/*
 -		 * No strings still has a double-null at the end,
 -		 * skip over it
 +		 * Look for a double-nul after the end of the
 +		 * formatted area of this structure.
  		 */
 -		if (table[0] == 0)
 -			nextp++;
 +		p += s->length;
 +		while (p[0] != 0 && p[1] != 0)
 +			p++;
  
 -		if (dispatchstatus[*p]) {
 -			(dispatchstatus[*p])(p, table, info);
 -		}
 -		p = nextp;
 +		/*
 +		 * Skip over the double-nul to the start of the next
 +		 * structure.
 +		 */
 +		p += 2;
  	}
  }
  
 @@ -221,8 +208,7 @@
  static void
  ipmi_smbios_probe(struct ipmi_get_info *info)
  {
 -	dispatchproc_t dispatch_smbios_ipmi[256];
 -	struct smbios_table_entry *header;
 +	struct smbios_table *header;
  	void *table;
  	u_int32_t addr;
  
 @@ -239,9 +225,9 @@
  	 * length and then map it a second time with the actual length so
  	 * we can verify the checksum.
  	 */
 -	header = pmap_mapbios(addr, sizeof(struct smbios_table_entry));
 +	header = pmap_mapbios(addr, sizeof(struct smbios_table));
  	table = pmap_mapbios(addr, header->length);
 -	pmap_unmapbios((vm_offset_t)header, sizeof(struct smbios_table_entry));
 +	pmap_unmapbios((vm_offset_t)header, sizeof(struct smbios_table));
  	header = table;
  	if (smbios_cksum(header) != 0) {
  		pmap_unmapbios((vm_offset_t)header, header->length);
 @@ -251,9 +237,7 @@
  	/* Now map the actual table and walk it looking for an IPMI entry. */
  	table = pmap_mapbios(header->structure_table_address,
  	    header->structure_table_length);
 -	bzero((void *)dispatch_smbios_ipmi, sizeof(dispatch_smbios_ipmi));
 -	dispatch_smbios_ipmi[38] = (void *)smbios_t38_proc_info;
 -	smbios_run_table(table, header->number_structures, dispatch_smbios_ipmi,
 +	smbios_walk_table(table, header->number_structures, smbios_ipmi_info,
  	    info);
  
  	/* Unmap everything. */
 @@ -298,7 +282,7 @@
  }
  
  static int
 -smbios_cksum (struct smbios_table_entry *e)
 +smbios_cksum(struct smbios_table *e)
  {
  	u_int8_t *ptr;
  	u_int8_t cksum;
 
 -- 
 John Baldwin


More information about the freebsd-bugs mailing list