Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 Jul 2010 18:06:21 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r210066 - head/sys/dev/ipmi
Message-ID:  <201007141806.o6EI6LWK039888@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Wed Jul 14 18:06:21 2010
New Revision: 210066
URL: http://svn.freebsd.org/changeset/base/210066

Log:
  Rework the SMBIOS table walker to make it operate like other table walkers
  and remove a buffer overflow:
  - Remove the array of per-type dispatch functions.  Instead, pass each
    structure to a single callback.  The callback should check the type of
    each table entry to take appropriate action.  This matches the behavior
    of other table walkers such as for the MP Table and MADT.
  - Don't attempt to save an array of string pointers for each structure
    entry.  Instead, just skip the strings.  If this code is reused to
    provide a generic SMBIOS table walker in the future we could provide
    a method that looks up a specific string N for a given structure record
    instead of pre-populating an array of pointers.  This fixes a buffer
    overflow for structure entries with more than 20 strings.
  
  PR:		kern/148546
  Reported by:	Spencer Minear @ McAfee
  MFC after:	3 days

Modified:
  head/sys/dev/ipmi/ipmi_smbios.c

Modified: head/sys/dev/ipmi/ipmi_smbios.c
==============================================================================
--- head/sys/dev/ipmi/ipmi_smbios.c	Wed Jul 14 17:46:44 2010	(r210065)
+++ head/sys/dev/ipmi/ipmi_smbios.c	Wed Jul 14 18:06:21 2010	(r210066)
@@ -52,7 +52,7 @@ __FBSDID("$FreeBSD$");
 #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 @@ struct ipmi_entry {
 #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 @@ smbios_t38_proc_info(uint8_t *p, char **
 	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 @@ smbios_run_table(uint8_t *p, int entries
 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 @@ ipmi_smbios_probe(struct ipmi_get_info *
 	 * 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 @@ ipmi_smbios_probe(struct ipmi_get_info *
 	/* 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 @@ ipmi_smbios_identify(struct ipmi_get_inf
 }
 
 static int
-smbios_cksum (struct smbios_table_entry *e)
+smbios_cksum(struct smbios_table *e)
 {
 	u_int8_t *ptr;
 	u_int8_t cksum;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201007141806.o6EI6LWK039888>