From cb43f03fada911d6ae1caad9141ee3cfc9e96184 Mon Sep 17 00:00:00 2001 From: Huzz Date: Sat, 3 Sep 2022 20:03:27 +0800 Subject: [PATCH] Fix reading info permission deny bugs. Details: 1. Change default /dev/mem to sysfs tables, if /dev/mem is unavailable, use sysfs tables instead. 2. Modify init method from dmidecodemodule. 3. Add read_file function instead memchunk. 4. Add SM3 reading function and get version function. 5. Test physical machine and KVM machine --- src/dmidecode.c | 117 ++++++++++---- src/dmidecode.h | 13 +- src/dmidecodemodule.c | 367 ++++++++++++++++++++++++++++++------------ src/dmidecodemodule.h | 6 +- src/dmidump.c | 272 +++++++++++++++++++------------ src/dmidump.h | 7 + src/util.c | 166 +++++++++++++++++-- src/util.h | 1 + 8 files changed, 703 insertions(+), 246 deletions(-) diff --git a/src/dmidecode.c b/src/dmidecode.c index d30a5d3..19958d3 100644 --- a/src/dmidecode.c +++ b/src/dmidecode.c @@ -5197,7 +5197,7 @@ dmi_codes_major *find_dmiMajor(const struct dmi_header *h) return NULL; } -static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver, const char *devmem, xmlNode *xmlnode) +static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver, const char *devmem, u32 flags, xmlNode *xmlnode) { static u8 version_added = 0; u8 *buf; @@ -5221,35 +5221,40 @@ static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver info_n = NULL; } - if((buf = mem_chunk(logp, base, len, devmem)) == NULL) { - log_append(logp, LOGFL_NODUPS, LOG_WARNING, "Table is unreachable, sorry." -#ifndef USE_MMAP - "Try compiling dmidecode with -DUSE_MMAP." -#endif - ); - return; - } + if(flags & FLAG_NO_FILE_OFFSET){ + /* + * When reading from sysfs or from a dump file, the file may be + * shorter than announced. For SMBIOS v3 this is expcted, as we + * only know the maximum table size, not the actual table size. + * For older implementations (and for SMBIOS v3 too), this + * would be the result of the kernel truncating the table on + * parse error. + */ + size_t size = len; + buf = read_file(logp, flags & FLAG_NO_FILE_OFFSET ? 0 : base, &size, devmem); + if (num && size != (size_t)len){ + log_append(logp, LOGFL_NODUPS, LOG_WARNING, "Wrong DMI structures length: %i bytes announced, only %lu bytes available.\n", len, (unsigned long)size ); + } + len = size; + } else { + buf = mem_chunk(logp, base, len, devmem); + } + + if (ver > SUPPORTED_SMBIOS_VER){ + log_append(logp, LOGFL_NODUPS, LOG_WARNING, "# SMBIOS implementations newer than version %u.%u are not\n", "# fully supported by this version of dmidecode.\n", SUPPORTED_SMBIOS_VER >> 8, SUPPORTED_SMBIOS_VER & 0xFF); + } - if (ver > SUPPORTED_SMBIOS_VER) { - log_append(logp, LOGFL_NODUPS, LOG_WARNING, - "# SMBIOS implementations newer than version %u.%u are not\n" - "# fully supported by this version of dmidecode.\n", - SUPPORTED_SMBIOS_VER >> 8, SUPPORTED_SMBIOS_VER & 0xFF); - } - // FIXME: This is hackerish ... rather try to avoid looping dmi_table() calls too much if( version_added == 0 ) { dmixml_AddAttribute(xmlnode, "smbios_version", "%u.%u", ver >> 8, ver & 0xFF); version_added = 1; } - data = buf; - while(i < num && data + 4 <= buf + len) { /* 4 is the length of an SMBIOS structure header */ - + data = buf; + while(i < num && data + 4 <= buf + len) { /* 4 is the length of an SMBIOS structure header */ u8 *next; struct dmi_header h; to_dmi_header(&h, data); - /* ** If a short entry is found (less than 4 bytes), not only it ** is invalid, but we cannot reliably locate the next entry. @@ -5258,8 +5263,8 @@ static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver */ if(h.length < 4) { log_append(logp, LOGFL_NORMAL, LOG_WARNING, - "Invalid entry length (%i) for type %i. DMI table is broken! Stop.", - (unsigned int)h.length, type); + "Invalid entry length (%i) for type %i. DMI table is broken! Stop.", + (unsigned int)h.length, type); break; } @@ -5278,7 +5283,6 @@ static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver next++; } next += 2; - xmlNode *handle_n = NULL; if( h.type == type ) { if(next - buf <= len) { @@ -5309,6 +5313,7 @@ static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver dmixml_AddAttribute(handle_n, "length", "%i", (next - buf)); dmixml_AddAttribute(handle_n, "expected_length", "%i", len); + log_append(logp, LOGFL_NODUPS, LOG_WARNING, "DMI/SMBIOS type 0x%02X is exceeding the expected buffer " "size by %i bytes. Will not decode this entry.", @@ -5344,6 +5349,60 @@ static void dmi_table(Log_t *logp, int type, u32 base, u16 len, u16 num, u16 ver free(buf); } +int _smbios3_decode_check(u8 *buf){ + int check = (!checksum(buf, buf[0x06])) ? 0 : 1; + return check; +} + +xmlNode *smbios3_decode_get_version(u8 * buf, const char *devmem) +{ + int check = _smbios3_decode_check(buf); + + xmlNode *data_n = xmlNewNode(NULL, (xmlChar *) "DMIversion"); + assert( data_n != NULL ); + + dmixml_AddAttribute(data_n, "type", "SMBIOS"); + + if(check == 1) { + u16 ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09]; + + dmixml_AddTextContent(data_n, "SMBIOS %i.%i.%i present", buf[0x07], buf[0x08], buf[0x09]); + dmixml_AddAttribute(data_n, "version", "%i.%i.%i", buf[0x07], buf[0x08],buf[0x09]); + } else if(check == 0) { + dmixml_AddTextContent(data_n, "No SMBIOS nor DMI entry point found"); + dmixml_AddAttribute(data_n, "unknown", "1"); + } + return data_n; +} + +int smbios3_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *xmlnode) +{ + u32 ver; + u64 offset; + + /* Don't let checksum run beyond the buffer */ + if (buf[0x06] > 0x20) + { + return 0; + } + + int check = _smbios3_decode_check(buf); + if (check == 1) + { + ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09]; + offset = QWORD(buf + 0x10); + + if (!(flags & FLAG_NO_FILE_OFFSET) && offset.h && sizeof(off_t) < 8) + { + return 0; + } + + dmi_table(logp, type, ((off_t)offset.h << 32) | offset.l, DWORD(buf+0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT, xmlnode); + } + + return check; +} + int _smbios_decode_check(u8 * buf) { int check = (!checksum(buf, buf[0x05]) || memcmp(buf + 0x10, "_DMI_", 5) != 0 || @@ -5370,7 +5429,8 @@ xmlNode *smbios_decode_get_version(u8 * buf, const char *devmem) _M = 0; switch (ver) { case 0x021F: - _m = 31; + case 0x0221: + _m = ver & 0xFF; _M = 3; ver = 0x0203; break; @@ -5396,7 +5456,7 @@ xmlNode *smbios_decode_get_version(u8 * buf, const char *devmem) return data_n; } -int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *xmlnode) +int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *xmlnode) { int check = _smbios_decode_check(buf); @@ -5405,6 +5465,7 @@ int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *x switch (ver) { case 0x021F: + case 0x0221: ver = 0x0203; break; case 0x0233: @@ -5413,7 +5474,7 @@ int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *x } // printf(">>%d @ %d, %d<<\n", DWORD(buf+0x18), WORD(buf+0x16), WORD(buf+0x1C)); dmi_table(logp, type, DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C), ver, devmem, - xmlnode); + flags, xmlnode); } return check; } @@ -5451,13 +5512,13 @@ xmlNode *legacy_decode_get_version(u8 * buf, const char *devmem) return data_n; } -int legacy_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *xmlnode) +int legacy_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *xmlnode) { int check = _legacy_decode_check(buf); if(check == 1) dmi_table(logp, type, DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C), - ((buf[0x0E] & 0xF0) << 4) + (buf[0x0E] & 0x0F), devmem, xmlnode); + ((buf[0x0E] & 0xF0) << 4) + (buf[0x0E] & 0x0F), devmem, flags, xmlnode); return check; } diff --git a/src/dmidecode.h b/src/dmidecode.h index 926bd5d..22d70e1 100644 --- a/src/dmidecode.h +++ b/src/dmidecode.h @@ -23,6 +23,13 @@ #include "dmihelper.h" #include "dmierror.h" +#define FLAG_NO_FILE_OFFSET (1 << 0) +#define FLAG_STOP_AT_EOT (1 << 1) + +#define SYS_FIRMWARE_DIR "/sys/firmware/dmi/tables" +#define SYS_ENTRY_FILE SYS_FIRMWARE_DIR "/smbios_entry_point" +#define SYS_TABLE_FILE SYS_FIRMWARE_DIR "/DMI" + struct dmi_header { u8 type; u8 length; @@ -34,10 +41,12 @@ void dmi_dump(xmlNode *node, struct dmi_header * h); xmlNode *dmi_decode(xmlNode *parent_n, dmi_codes_major *dmiMajor, struct dmi_header * h, u16 ver); void to_dmi_header(struct dmi_header *h, u8 * data); +xmlNode *smbios3_decode_get_version(u8 * buf, const char *devmem); xmlNode *smbios_decode_get_version(u8 * buf, const char *devmem); xmlNode *legacy_decode_get_version(u8 * buf, const char *devmem); -int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *xmlnode); -int legacy_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *xmlnode); +int smbios3_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *xmlnode); +int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *xmlnode); +int legacy_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *xmlnode); const char *dmi_string(const struct dmi_header *dm, u8 s); void dmi_system_uuid(xmlNode *node, const u8 * p, u16 ver); diff --git a/src/dmidecodemodule.c b/src/dmidecodemodule.c index 44ef7aa..d3ac7ff 100644 --- a/src/dmidecodemodule.c +++ b/src/dmidecodemodule.c @@ -66,7 +66,9 @@ char *PyUnicode_AsUTF8(PyObject *unicode) { static void init(options *opt) { - opt->devmem = DEFAULT_MEM_DEV; + int efi; + size_t fp; + opt->dumpfile = NULL; opt->flags = 0; opt->type = -1; @@ -75,6 +77,13 @@ static void init(options *opt) opt->python_xml_map = strdup(PYTHON_XML_MAP); opt->logdata = log_init(); + efi = address_from_efi(opt->logdata, &fp); + if(efi == EFI_NOT_FOUND){ + opt->devmem = DEFAULT_MEM_DEV; + } else { + opt->devmem = SYS_TABLE_FILE; + } + /* sanity check */ if(sizeof(u8) != 1 || sizeof(u16) != 2 || sizeof(u32) != 4 || '\0' != 0) { log_append(opt->logdata, LOGFL_NORMAL, LOG_WARNING, @@ -116,70 +125,149 @@ xmlNode *dmidecode_get_version(options *opt) int efi; u8 *buf = NULL; xmlNode *ver_n = NULL; + size_t size; - /* Set default option values */ + /* + * First, if devmem is available, set default as DEFAULT_MEM_DEV + * Set default option values + */ if( opt->devmem == NULL ) { - opt->devmem = DEFAULT_MEM_DEV; + efi = address_from_efi(opt->logdata, &fp); + if(efi == EFI_NOT_FOUND){ + opt->devmem = DEFAULT_MEM_DEV; + } else { + opt->devmem = SYS_TABLE_FILE; + } } /* Read from dump if so instructed */ if(opt->dumpfile != NULL) { //. printf("Reading SMBIOS/DMI data from file %s.\n", dumpfile); if((buf = mem_chunk(opt->logdata, 0, 0x20, opt->dumpfile)) != NULL) { - if(memcmp(buf, "_SM_", 4) == 0) { - ver_n = smbios_decode_get_version(buf, opt->dumpfile); - if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { - found++; - } - } else if(memcmp(buf, "_DMI_", 5) == 0) { - ver_n = legacy_decode_get_version(buf, opt->dumpfile); - if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { - found++; - } - } - } - } else { /* Read from /dev/mem */ - /* First try EFI (ia64, Intel-based Mac) */ - efi = address_from_efi(opt->logdata, &fp); - if(efi == EFI_NOT_FOUND) { - /* Fallback to memory scan (x86, x86_64) */ - if((buf = mem_chunk(opt->logdata, 0xF0000, 0x10000, opt->devmem)) != NULL) { - for(fp = 0; fp <= 0xFFF0; fp += 16) { - if(memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { - ver_n = smbios_decode_get_version(buf + fp, opt->devmem); - if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { - found++; - } - fp += 16; - } else if(memcmp(buf + fp, "_DMI_", 5) == 0) { - ver_n = legacy_decode_get_version (buf + fp, opt->devmem); - if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { - found++; - } - } - } - } - } else if(efi == EFI_NO_SMBIOS) { - ver_n = NULL; - } else { - // Process as EFI - if((buf = mem_chunk(opt->logdata, fp, 0x20, opt->devmem)) != NULL) { - ver_n = smbios_decode_get_version(buf, opt->devmem); - if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { - found++; - } - //. TODO: dmixml_AddAttribute(dmixml_n, "efi_address", efiAddress); - } + ver_n = NULL; + goto exit_free; + } + if(memcmp(buf, "_SM3_", 5) == 0){ + ver_n = smbios3_decode_get_version(buf, opt->dumpfile); + if ( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) + found++; + } else if(memcmp(buf, "_SM_", 4) == 0) { + ver_n = smbios_decode_get_version(buf, opt->dumpfile); + if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) + found++; + } else if(memcmp(buf, "_DMI_", 5) == 0) { + ver_n = legacy_decode_get_version(buf, opt->dumpfile); + if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) + found++; } } - if( buf != NULL ) { - free(buf); - } - if( !found ) { + + /* + * First try reading from sysfs tables. The entry point file could + * contain one of several types of entry points, so read enough for + * the largest one, then determine what type it contains. + */ + size = 0x20; + if ( (buf = read_file(opt->logdata, 0, &size, SYS_ENTRY_FILE)) != NULL ){ + if(size >= 24 && memcmp(buf, "_SM3_", 5) == 0){ + ver_n = smbios3_decode_get_version(buf, opt->devmem); + if (dmixml_GetAttrValue(ver_n, "unknown") == NULL) + found++; + } else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0 ){ + ver_n = smbios_decode_get_version(buf, opt->devmem); + if (dmixml_GetAttrValue(ver_n, "unknown") == NULL) + found++; + } else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0){ + ver_n = legacy_decode_get_version (buf, opt->devmem); + if (dmixml_GetAttrValue(ver_n, "unknown") == NULL) + found++; + } + + if(found) + goto done; + } else { + ver_n = NULL; + goto exit_free; + } + + /* Read from /dev/mem */ + /* Next try EFI (ia64, Intel-based Mac) */ + efi = address_from_efi(opt->logdata, &fp); + switch(efi){ + case EFI_NOT_FOUND: + goto memory_scan; + case EFI_NO_SMBIOS: + ver_n = NULL; + goto exit_free; + } + + if(buf = mem_chunk(opt->logdata, fp, 0x20, opt->devmem) == NULL){ + ver_n = NULL; + goto exit_free; + } + + if(memcmp(buf, "_SM3_", 5) == 0){ + ver_n = smbios3_decode_get_version(buf, opt->devmem); + if(dmixml_GetAttrValue(ver_n, "unknown") == NULL) + found++; + } else if (memcmp(buf, "_SM_", 4) == 0 ) { + ver_n = smbios_decode_get_version(buf, opt->devmem); + if(dmixml_GetAttrValue(ver_n, "unknown") == NULL) + found++; + } + + goto done; + +memory_scan: +#if defined __i386__ || defined __x86_64__ + /* Fallback to memory scan (x86, x86_64) */ + if((buf = mem_chunk(opt->logdata, 0xF0000, 0x10000, opt->devmem)) == NULL) { + ver_n = NULL; + goto exit_free; + } + + /* Look for a 64-bit entry point first */ + for (fp = 0; fp <= 0xFFE0; fp+= 16){ + if(memcmp(buf+fp, "_SM3_", 5) == 0){ + ver_n = smbios3_decode_get_version(buf+fp, opt->devmem); + if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { + found++; + goto done; + } + } + } + + /* If none found, look for a 32-bit entry point */ + for(fp = 0; fp <= 0xFFF0; fp += 16) { + if(memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { + ver_n = smbios_decode_get_version(buf + fp, opt->devmem); + if ( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { + found++; + goto done; + } + fp += 16; + } else if (memcmp(buf + fp, "_DMI_", 5) == 0) { + ver_n = legacy_decode_get_version (buf + fp, opt->devmem); + if( dmixml_GetAttrValue(ver_n, "unknown") == NULL ) { + found++; + goto done; + } + } + } +#endif + +done: + if(!found){ log_append(opt->logdata, LOGFL_NODUPS, LOG_WARNING, - "No SMBIOS nor DMI entry point found, sorry."); - } - return ver_n; + "No SMBIOS nor DMI entry point found, sorry."); + } + +exit_free: + if (buf != NULL) + free(buf); + + return ver_n; + } int dmidecode_get_xml(options *opt, xmlNode* dmixml_n) @@ -195,6 +283,7 @@ int dmidecode_get_xml(options *opt, xmlNode* dmixml_n) size_t fp; int efi; u8 *buf = NULL; + size_t size; const char *f = opt->dumpfile ? opt->dumpfile : opt->devmem; if(access(f, R_OK) < 0) { @@ -205,53 +294,119 @@ int dmidecode_get_xml(options *opt, xmlNode* dmixml_n) /* Read from dump if so instructed */ if(opt->dumpfile != NULL) { - // printf("Reading SMBIOS/DMI data from file %s.\n", dumpfile); - if((buf = mem_chunk(opt->logdata, 0, 0x20, opt->dumpfile)) != NULL) { - if(memcmp(buf, "_SM_", 4) == 0) { - if(smbios_decode(opt->logdata, opt->type, buf, opt->dumpfile, dmixml_n)) - found++; - } else if(memcmp(buf, "_DMI_", 5) == 0) { - if(legacy_decode(opt->logdata, opt->type, buf, opt->dumpfile, dmixml_n)) - found++; + if((buf = mem_chunk(opt->logdata, 0, 0x20, opt->dumpfile)) == NULL) { + ret = 1; + goto exit_free; + } + if(memcmp(buf, "_SM3_", 5) == 0){ + if(smbios3_decode(opt->logdata, opt->type, buf,opt->dumpfile, 0, dmixml_n)) + found++; + } else if (memcmp(buf, "_SM_", 4) == 0){ + if(smbios_decode(opt->logdata, opt->type, buf, opt->dumpfile, 0, dmixml_n)) + found++; + } else if (memcmp(buf, "_DMI_", 5) == 0){ + if(legacy_decode(opt->logdata, opt->type, buf, opt->dumpfile, 0, dmixml_n)) + found++; + } + goto done; + } + + /* + * First try reading from sysfs tables. The entry point file could + * contain one of several types of entry points, so read enough for + * the largest one, then determine what type it contains. + */ + size = 0x20; + if ( (buf = read_file(opt->logdata, 0, &size, SYS_ENTRY_FILE)) != NULL){ + if ( size >= 24 && memcmp(buf, "_SM3_", 5) == 0) { + if (smbios3_decode(opt->logdata, opt->type, buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET, dmixml_n)) + found++; + } else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0){ + if (smbios_decode(opt->logdata, opt->type, buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET, dmixml_n)) + found++; + } else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0){ + if (legacy_decode(opt->logdata, opt->type, buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET, dmixml_n)) + found++; + } + if (found) + goto done; + } else { + ret = 1; + goto done; + } + + /* Next try EFI (ia64, Intel-based Mac) */ + efi = address_from_efi(opt->logdata, &fp); + switch(efi){ + case EFI_NOT_FOUND: + goto memory_scan; + case EFI_NO_SMBIOS: + ret = 1; + goto exit_free; + } + + if(buf = mem_chunk(opt->logdata, fp, 0x20, opt->devmem) == NULL ){ + ret = 1; + goto exit_free; + } + + if (memcmp(buf, "_SM3_", 5) == 0){ + if (smbios3_decode(opt->logdata, opt->type, buf + fp, opt->devmem, 0, dmixml_n)) + found++; + } else if (memcmp(buf, "_SM_", 4) == 0){ + if(smbios_decode(opt->logdata, opt->type, buf + fp, opt->devmem, 0, dmixml_n)) + found++; + } + + goto done; + +memory_scan: +#if defined __i386__ || defined __x86_64__ + if((buf = mem_chunk(opt->logdata, 0xF0000, 0x10000, opt->devmem)) == NULL) + { + ret = 1; + goto exit_free; + } + + /* Look for a 64-bit entry point first */ + for (fp = 0; fp <= 0xFFE0; fp += 16){ + if (memcmp(buf + fp, "_SM3_", 5) == 0) + { + if(smbios3_decode(opt->logdata, opt->type, + buf + fp, opt->devmem, 0, dmixml_n)){ + found++; + goto done; } - } else { - ret = 1; } - } else { /* Read from /dev/mem */ - /* First try EFI (ia64, Intel-based Mac) */ - efi = address_from_efi(opt->logdata, &fp); - if(efi == EFI_NOT_FOUND) { - /* Fallback to memory scan (x86, x86_64) */ - if((buf = mem_chunk(opt->logdata, 0xF0000, 0x10000, opt->devmem)) != NULL) { - for(fp = 0; fp <= 0xFFF0; fp += 16) { - if(memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { - if(smbios_decode(opt->logdata, opt->type, - buf + fp, opt->devmem, dmixml_n)) { - found++; - fp += 16; - } - } else if(memcmp(buf + fp, "_DMI_", 5) == 0) { - if(legacy_decode(opt->logdata, opt->type, - buf + fp, opt->devmem, dmixml_n)) - found++; - } - } - } else - ret = 1; - } else if(efi == EFI_NO_SMBIOS) { - ret = 1; - } else { - if((buf = mem_chunk(opt->logdata, fp, 0x20, opt->devmem)) == NULL) - ret = 1; - else if(smbios_decode(opt->logdata, opt->type, buf, opt->devmem, dmixml_n)) + } + + /* If none found, look for a 32-bit entry point */ + for(fp = 0; fp <= 0xFFF0; fp += 16) { + if(memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { + if(smbios_decode(opt->logdata, opt->type, + buf + fp, opt->devmem, 0, dmixml_n)) { + found++; + goto done; + } + } else if(memcmp(buf + fp, "_DMI_", 5) == 0) { + if(legacy_decode(opt->logdata, opt->type, + buf + fp, opt->devmem, 0, dmixml_n)) found++; - // TODO: dmixml_AddAttribute(dmixml_n, "efi_address", "0x%08x", efiAddress); + goto done; + } } +#endif + +done: + if( !found ) { + log_append(opt->logdata, LOGFL_NODUPS, LOG_WARNING, + "No SMBIOS nor DMI entry point found, sorry."); } - if(ret == 0) { + +exit_free: + if(buf != NULL) free(buf); - } - //muntrace(); + return ret; } @@ -342,14 +497,21 @@ xmlNode *__dmidecode_xml_getsection(options *opt, const char *section) { static PyObject *dmidecode_get_group(options *opt, const char *section) { + int efi; + size_t fp; PyObject *pydata = NULL; xmlNode *dmixml_n = NULL; ptzMAP *mapping = NULL; /* Set default option values */ if( opt->devmem == NULL ) { - opt->devmem = DEFAULT_MEM_DEV; - } + efi = address_from_efi(opt->logdata, &fp); + if(efi == EFI_NOT_FOUND){ + opt->devmem = DEFAULT_MEM_DEV; + } else { + opt->devmem = SYS_TABLE_FILE; + } + } opt->flags = 0; // Decode the dmidata into an XML node @@ -380,11 +542,18 @@ static PyObject *dmidecode_get_group(options *opt, const char *section) xmlNode *__dmidecode_xml_gettypeid(options *opt, int typeid) { + int efi; + size_t fp; xmlNode *dmixml_n = NULL; /* Set default option values */ if( opt->devmem == NULL ) { - opt->devmem = DEFAULT_MEM_DEV; + efi = address_from_efi(opt->logdata, &fp); + if(efi == EFI_NOT_FOUND){ + opt->devmem = DEFAULT_MEM_DEV; + } else { + opt->devmem = SYS_TABLE_FILE; + } } opt->flags = 0; @@ -598,7 +767,7 @@ static PyObject *dmidecode_dump(PyObject * self, PyObject * null) stat(f, &_buf); if( (access(f, F_OK) != 0) || ((access(f, W_OK) == 0) && S_ISREG(_buf.st_mode)) ) { - if( dump(DEFAULT_MEM_DEV, f) ) { + if( dump(SYS_TABLE_FILE, f) ) { Py_RETURN_TRUE; } } diff --git a/src/dmidecodemodule.h b/src/dmidecodemodule.h index 3600ba9..044317e 100644 --- a/src/dmidecodemodule.h +++ b/src/dmidecodemodule.h @@ -68,8 +68,10 @@ xmlNode *dmidecode_get_version(options *); extern void dmi_dump(xmlNode *node, struct dmi_header *h); extern int address_from_efi(Log_t *logp, size_t * address); extern void to_dmi_header(struct dmi_header *h, u8 * data); -extern int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *node); -extern int legacy_decode(Log_t *logp, int type, u8 *buf, const char *devmem, xmlNode *node); +extern int smbios3_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *node); +extern int smbios_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *node); +extern int legacy_decode(Log_t *logp, int type, u8 *buf, const char *devmem, u32 flags, xmlNode *node); +extern xmlNode *smbios3_decode_get_version(u8 * buf, const char *devmem); extern xmlNode *smbios_decode_get_version(u8 * buf, const char *devmem); extern xmlNode *legacy_decode_get_version(u8 * buf, const char *devmem); extern void *mem_chunk(Log_t *logp, size_t base, size_t len, const char *devmem); diff --git a/src/dmidump.c b/src/dmidump.c index fc67481..0d052fc 100644 --- a/src/dmidump.c +++ b/src/dmidump.c @@ -51,144 +51,214 @@ static void overwrite_dmi_address(u8 * buf) buf[0x0B] = 0; } +/* Same thing for SMBIOS3 entry points */ +static void overwrite_smbios3_address(u8 *buf) +{ + buf[0x05] += buf[0x10] + buf[0x11] + buf[0x12] + buf[0x13] + + buf[0x14] + buf[0x15] + buf[0x16] + buf[0x17] - 32; + buf[0x10] = 32; + buf[0x11] = 0; + buf[0x12] = 0; + buf[0x13] = 0; + buf[0x14] = 0; + buf[0x15] = 0; + buf[0x16] = 0; + buf[0x17] = 0; +} -int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add) +void dmi_table_dump(const u8 *buf, u32 len, const char *dumpfile) { - FILE *f; + write_dump(32, len, buf, dumpfile, 0); +} - f = fopen(dumpfile, add ? "r+b" : "wb"); - if(!f) { - fprintf(stderr, "%s: ", dumpfile); - perror("fopen"); - return -1; - } +void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem, + u32 flags, const char *dumpfile) +{ + u8 *buf; + size_t size = len; - if(fseek(f, base, SEEK_SET) != 0) { - fprintf(stderr, "%s: ", dumpfile); - perror("fseek"); - goto err_close; - } + buf = read_file(NULL, flags & FLAG_NO_FILE_OFFSET ? 0 : base, + &size, devmem); + len = size; - if(fwrite(data, len, 1, f) != 1) { - fprintf(stderr, "%s: ", dumpfile); - perror("fwrite"); - goto err_close; + if (buf == NULL) + { + printf("read failed\n"); } + dmi_table_dump(buf, len, dumpfile); + free(buf); +} - if(fclose(f)) { - fprintf(stderr, "%s: ", dumpfile); - perror("fclose"); - return -1; - } +static int smbios3_decode(u8 *buf, const char *devmem, u32 flags, const char *dumpfile) +{ + u32 ver; + u64 offset; + offset = QWORD(buf + 0x10); + ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09]; - return 0; + dmi_table(((off_t)offset.h << 32) | offset.l,DWORD(buf + 0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT, dumpfile); - err_close: - fclose(f); - return -1; -} + if (!checksum(buf, buf[0x05])) + return 0; + u8 crafted[32]; + memcpy(crafted, buf, 32); + overwrite_smbios3_address(crafted); + //overwrite_dmi_address(crafted); + //printf("Writing %d bytes to %s.",crafted[0x06], dumpfile); + write_dump(0, crafted[0x06], crafted, dumpfile, 1); + return 1; +} -int dumpling(u8 * buf, const char *dumpfile, u8 mode) +static int smbios_decode(u8 *buf, const char *devmem, u32 flags, const char *dumpfile) { - u32 base; - u16 len; - - if(mode == NON_LEGACY) { - if(!checksum(buf, buf[0x05]) || !memcmp(buf + 0x10, "_DMI_", 5) == 0 || - !checksum(buf + 0x10, 0x0F)) - return 0; - base = DWORD(buf + 0x18); - len = WORD(buf + 0x16); - } else { - if(!checksum(buf, 0x0F)) - return 0; - base = DWORD(buf + 0x08); - len = WORD(buf + 0x06); - } + u16 ver; + if (!checksum(buf, buf[0x05]) + || memcmp(buf + 0x10, "_DMI_", 5) != 0 + || !checksum(buf + 0x10, 0x0F)) + return 0; - u8 *buff; + ver = (buf[0x06] << 8) + buf[0x07]; + switch (ver) + { + case 0x021F: + case 0x0221: + ver = 0x0203; + break; + case 0x0233: + ver = 0x0206; + break; + } - if((buff = mem_chunk(NULL, base, len, DEFAULT_MEM_DEV)) != NULL) { - //. Part 1. -#ifdef NDEBUG - printf("# Writing %d bytes to %s.\n", len, dumpfile); -#endif - write_dump(32, len, buff, dumpfile, 0); - free(buff); - //. Part 2. - if(mode != LEGACY) { - u8 crafted[32]; + dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C), + ver << 8, devmem, flags, dumpfile); - memcpy(crafted, buf, 32); - overwrite_dmi_address(crafted + 0x10); -#ifdef NDEBUG - printf("# Writing %d bytes to %s.\n", crafted[0x05], dumpfile); -#endif - write_dump(0, crafted[0x05], crafted, dumpfile, 1); - } else { - u8 crafted[16]; - - memcpy(crafted, buf, 16); - overwrite_dmi_address(crafted); -#ifdef NDEBUG - printf("# Writing %d bytes to %s.\n", 0x0F, dumpfile); -#endif - write_dump(0, 0x0F, crafted, dumpfile, 1); - } - } else { - fprintf(stderr, "Failed to read table, sorry.\n"); - } + u8 crafted[32]; + memcpy(crafted, buf, 32); + overwrite_dmi_address(crafted + 0x10); + write_dump(0, crafted[0x05], crafted, dumpfile, 1); - //. TODO: Cleanup return 1; } +static int legacy_decode(u8 *buf, const char *devmem, u32 flags, const char *dumpfile) +{ + u8 crafted[16]; + + //dmi_table(); + dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C), + ((buf[0x0E] & 0xF0) << 12) + ((buf[0x0E] & 0x0F) << 8), + devmem, flags, dumpfile); + + memcpy(crafted, buf, 16); + overwrite_smbios3_address(crafted); + write_dump(0, 0x0F, crafted, dumpfile, 1); +} int dump(const char *memdev, const char *dumpfile) { - /* On success, return found, otherwise return -1 */ + /* On success, return found, otherwise return 0 */ int ret = 0; int found = 0; size_t fp; int efi; u8 *buf; + size_t size; + + /* + * First try reading from sysfs tables. The entry point file could + * contain one of several types of entry points, so read enough for + * the largest one, then determine what type it contains. + */ + size = 0x20; + if ( (buf = read_file(NULL, 0, &size, SYS_ENTRY_FILE)) != NULL){ + if (size >= 24 && memcmp(buf, "_SM3_", 5) == 0){ + if (smbios3_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET, dumpfile)) + found++; + } else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0) { + if (smbios_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET, dumpfile)) + found++; + } else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0){ + if (legacy_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET, dumpfile)) + found++; + } + if (found){ + ret = 1; + goto exit_free; + } + } /* First try EFI (ia64, Intel-based Mac) */ efi = address_from_efi(NULL, &fp); - if(efi == EFI_NOT_FOUND) { - /* Fallback to memory scan (x86, x86_64) */ - if((buf = mem_chunk(NULL, 0xF0000, 0x10000, memdev)) != NULL) { - for(fp = 0; fp <= 0xFFF0; fp += 16) { - if(memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { - if(dumpling(buf + fp, dumpfile, NON_LEGACY)) - found++; - fp += 16; - } else if(memcmp(buf + fp, "_DMI_", 5) == 0) { - if(dumpling(buf + fp, dumpfile, LEGACY)) - found++; - } - } - } else - ret = -1; - } else if(efi == EFI_NO_SMBIOS) { - ret = -1; - } else { - if((buf = mem_chunk(NULL, fp, 0x20, memdev)) == NULL) - ret = -1; - else if(dumpling(buf, dumpfile, NON_LEGACY)) + switch(efi) + { + case EFI_NOT_FOUND: + goto memory_scan; + case EFI_NO_SMBIOS: + ret = 1; + goto exit_free; + } + + if ((buf = mem_chunk(NULL, fp, 0x20, memdev )) == NULL){ + ret = 1; + goto exit_free; + } + + if (memcmp(buf, "_SM3_", 5) == 0){ + if(smbios3_decode(buf, memdev, 0, dumpfile)) + found++; + } else if (memcmp(buf, "_SM_", 4) == 0){ + if(smbios_decode(buf, memdev, 0, dumpfile)) found++; } + goto done; - if(ret == 0) { - free(buf); - if(!found) { - ret = -1; +memory_scan: +#if defined __i386__ || defined __x86_64__ + /* Fallback to memory scan (x86, x86_64) */ + if((buf = mem_chunk(NULL, 0xF0000, 0x10000, memdev)) == NULL) { + ret = 1; + goto exit_free; + } + + /* Look for a 64-bit entry point first */ + for(fp = 0; fp <= 0xFFF0; fp += 16){ + if(memcmp(buf + fp, "_SM3_", 5) == 0 && fp <= 0xFFE0){ + if(smbios3_decode(buf + fp, memdev, 0, dumpfile)){ + found++; + goto done; + } + } + } + + /* If none found, look for a 32-bit entry point */ + for(fp = 0; fp <= 0xFFF0; fp += 16) { + if(memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { + if(smbios_decode(buf + fp, memdev, 0, dumpfile)){ + found++; + goto done; + } + } else if (memcmp(buf+fp, "_DMI_", 5) == 0){ + if(legacy_decode(buf+fp, memdev, 0, dumpfile)){ + found++; + goto done; + } } } +#endif + +done: + if(!found){ + printf("No SMBIOS nor DMI entry point found, sorry.\n"); + } + free(buf); + +exit_free: + if (!found) + free(buf); - return ret == 0 ? found : ret; + return ret; } diff --git a/src/dmidump.h b/src/dmidump.h index 3c12248..a025829 100644 --- a/src/dmidump.h +++ b/src/dmidump.h @@ -32,6 +32,13 @@ #define NON_LEGACY 0 #define LEGACY 1 +#define FLAG_NO_FILE_OFFSET (1 << 0) +#define FLAG_STOP_AT_EOT (1 << 1) + +#define SYS_FIRMWARE_DIR "/sys/firmware/dmi/tables" +#define SYS_ENTRY_FILE SYS_FIRMWARE_DIR "/smbios_entry_point" +#define SYS_TABLE_FILE SYS_FIRMWARE_DIR "/DMI" + int dump(const char *memdev, const char *dumpfile); #endif diff --git a/src/util.c b/src/util.c index da97767..4f233ff 100644 --- a/src/util.c +++ b/src/util.c @@ -50,7 +50,6 @@ #include "util.h" #include "dmilog.h" -#ifndef USE_MMAP static int myread(Log_t *logp, int fd, u8 * buf, size_t count, const char *prefix) { ssize_t r = 1; @@ -76,7 +75,6 @@ static int myread(Log_t *logp, int fd, u8 * buf, size_t count, const char *prefi return 0; } -#endif int checksum(const u8 * buf, size_t len) { @@ -88,6 +86,73 @@ int checksum(const u8 * buf, size_t len) return (sum == 0); } +/* + * Reads all of file from given offset, up to max_len bytes. + * A buffer of at most max_len bytes is allocated by this function, and + * needs to be freed by the caller. + * This provides a similar usage model to mem_chunk() + * + * Returns a pointer to the allocated buffer, or NULL on error, and + * sets max_len to the length actually read. + */ +void *read_file(Log_t *logp, off_t base, size_t *max_len, const char *filename) +{ + struct stat statbuf; + int fd; + u8 *p; + /* + * Don't print error message on missing file, as we will try to read + * files that may or may not be present. + */ + if ((fd = open(filename, O_RDONLY)) == -1) + { + if (errno != ENOENT) + perror(filename); + return NULL; + } + + /* + * Check file size, don't allocate more than can be read. + */ + if (fstat(fd, &statbuf) == 0) + { + if (base >= statbuf.st_size) + { + fprintf(stderr, "%s: Can't read data beyond EOF\n", + filename); + p = NULL; + goto out; + } + if (*max_len > (size_t)statbuf.st_size - base) + *max_len = statbuf.st_size - base; + } + + if ((p = malloc(*max_len)) == NULL) + { + perror("malloc"); + goto out; + } + + if (lseek(fd, base, SEEK_SET) == -1) + { + fprintf(stderr, "%s: ", filename); + perror("lseek"); + goto err_free; + } + if (myread(logp, fd, p, *max_len, filename) == 0) + goto out; + +err_free: + free(p); + p = NULL; + +out: + if (close(fd) == -1) + perror(filename); + + return p; +} + /* Static global variables which should only * be used by the sigill_handler() */ @@ -105,6 +170,18 @@ void sigill_handler(int ignore_this) { } } +static void safe_memcpy(void *dest, const void *src, size_t n) +{ +#ifdef USE_SLOW_MEMCPY + size_t i; + + for (i = 0; i < n; i++) + *((u8 *)dest + i) = *((const u8 *)src + i); +#else + memcpy(dest, src, n); +#endif +} + /* * Copy a physical memory chunk into a memory buffer. * This function allocates memory. @@ -115,7 +192,8 @@ void *mem_chunk(Log_t *logp, size_t base, size_t len, const char *devmem) int fd = -1; #ifdef USE_MMAP - size_t mmoffset; + struct stat statbuf; + size_t mmoffset; void *mmp; #endif sigill_logobj = logp; @@ -134,6 +212,23 @@ void *mem_chunk(Log_t *logp, size_t base, size_t len, const char *devmem) goto exit; } #ifdef USE_MMAP + if (sigill_error || fstat(fd, &statbuf) == -1 ) + { + log_append(logp, LOGFL_NORMAL, LOG_WARNING,"fstat: %s", strerror(errno)); + goto err_free; + } + + /* + * mmap() will fail with SIGBUS if trying to map beyond the end of + * the file. + */ + if (sigill_error || S_ISREG(statbuf.st_mode) && base + (off_t)len > statbuf.st_size ) + { + log_append(logp, LOGFL_NORMAL, LOG_WARNING, + "mmap: Can't map beyond end of file %s: %s", + devmem, strerror(errno)); + goto err_free; + } #ifdef _SC_PAGESIZE mmoffset = base % sysconf(_SC_PAGESIZE); #else @@ -147,9 +242,7 @@ void *mem_chunk(Log_t *logp, size_t base, size_t len, const char *devmem) mmp = mmap(0, mmoffset + len, PROT_READ, MAP_SHARED, fd, base - mmoffset); if(sigill_error || (mmp == MAP_FAILED)) { log_append(logp, LOGFL_NORMAL, LOG_WARNING, "%s (mmap): %s", devmem, strerror(errno)); - free(p); - p = NULL; - goto exit; + goto try_read; } memcpy(p, (u8 *) mmp + mmoffset, len); @@ -167,28 +260,34 @@ void *mem_chunk(Log_t *logp, size_t base, size_t len, const char *devmem) p = NULL; goto exit; } -#else /* USE_MMAP */ - if(sigill_error || (lseek(fd, base, SEEK_SET) == -1)) { + goto exit; + +try_read: +#endif /* USE_MMAP */ + if (lseek(fd, base, SEEK_SET) == -1 ) + { log_append(logp, LOGFL_NORMAL, LOG_WARNING, "%s (lseek): %s", devmem, strerror(errno)); - free(p); - p = NULL; - goto exit; + goto err_free; } - if(sigill_error || (myread(logp, fd, p, len, devmem) == -1)) { + if(sigill_error || (myread(logp, fd, p, len, devmem) == 0)) { free(p); p = NULL; goto exit; } -#endif /* USE_MMAP */ - exit: +err_free: + free(p); + p = NULL; + +exit: if (fd >= 0) { if(close(fd) == -1) perror(devmem); } signal(SIGILL, SIG_DFL); sigill_logobj = NULL; + return p; } @@ -207,3 +306,42 @@ u64 u64_range(u64 start, u64 end) return res; } + +int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add) +{ + FILE *f; + f = fopen(dumpfile, add ? "r+b" : "wb"); + if (!f) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fopen"); + return -1; + } + + if (fseek(f, base, SEEK_SET) != 0) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fseek"); + goto err_close; + } + + if (fwrite(data, len, 1, f) != 1) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fwrite"); + goto err_close; + } + + if (fclose(f)) + { + fprintf(stderr, "%s: ", dumpfile); + perror("fclose"); + return -1; + } + + return 0; + +err_close: + fclose(f); + return -1; +} diff --git a/src/util.h b/src/util.h index aa0487a..3c803c0 100644 --- a/src/util.h +++ b/src/util.h @@ -27,6 +27,7 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) int checksum(const u8 * buf, size_t len); +void *read_file( Log_t *logp, off_t base, size_t *len, const char *filename); void *mem_chunk(Log_t *logp, size_t base, size_t len, const char *devmem); int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add); u64 u64_range(u64 start, u64 end);