Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: Fix unaligned memory accesses in dmi-sysfs

DMI entries are arranged in memory back to back with no alignment
guarantees. This means that the struct dmi_header passed to callbacks
from dmi_walk() itself isn't byte aligned. This causes problems on
architectures that expect aligned data, such as IA64.

The dmi-sysfs patchset introduced structure member accesses through this
passed in dmi_header. Fix this by memcpy()ing the structures to
temporary locations on stack when inspecting/copying them.

Signed-off-by: Mike Waychison <mikew@google.com>
Tested-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Mike Waychison and committed by
Greg Kroah-Hartman
66245ad0 9effd822

+13 -15
+13 -15
drivers/firmware/dmi-sysfs.c
··· 263 263 u8 supported_log_type_descriptos[0]; 264 264 } __packed; 265 265 266 - static const struct dmi_system_event_log *to_sel(const struct dmi_header *dh) 267 - { 268 - return (const struct dmi_system_event_log *)dh; 269 - } 270 - 271 266 #define DMI_SYSFS_SEL_FIELD(_field) \ 272 267 static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \ 273 268 const struct dmi_header *dh, \ 274 269 char *buf) \ 275 270 { \ 276 - const struct dmi_system_event_log *sel = to_sel(dh); \ 277 - if (sizeof(*sel) > dmi_entry_length(dh)) \ 271 + struct dmi_system_event_log sel; \ 272 + if (sizeof(sel) > dmi_entry_length(dh)) \ 278 273 return -EIO; \ 279 - return sprintf(buf, "%u\n", sel->_field); \ 274 + memcpy(&sel, dh, sizeof(sel)); \ 275 + return sprintf(buf, "%u\n", sel._field); \ 280 276 } \ 281 277 static DMI_SYSFS_MAPPED_ATTR(sel, _field) 282 278 ··· 399 403 void *_state) 400 404 { 401 405 struct dmi_read_state *state = _state; 402 - const struct dmi_system_event_log *sel = to_sel(dh); 406 + struct dmi_system_event_log sel; 403 407 404 - if (sizeof(*sel) > dmi_entry_length(dh)) 408 + if (sizeof(sel) > dmi_entry_length(dh)) 405 409 return -EIO; 406 410 407 - switch (sel->access_method) { 411 + memcpy(&sel, dh, sizeof(sel)); 412 + 413 + switch (sel.access_method) { 408 414 case DMI_SEL_ACCESS_METHOD_IO8: 409 415 case DMI_SEL_ACCESS_METHOD_IO2x8: 410 416 case DMI_SEL_ACCESS_METHOD_IO16: 411 - return dmi_sel_raw_read_io(entry, sel, state->buf, 417 + return dmi_sel_raw_read_io(entry, &sel, state->buf, 412 418 state->pos, state->count); 413 419 case DMI_SEL_ACCESS_METHOD_PHYS32: 414 - return dmi_sel_raw_read_phys32(entry, sel, state->buf, 420 + return dmi_sel_raw_read_phys32(entry, &sel, state->buf, 415 421 state->pos, state->count); 416 422 case DMI_SEL_ACCESS_METHOD_GPNV: 417 423 pr_info("dmi-sysfs: GPNV support missing.\n"); 418 424 return -EIO; 419 425 default: 420 426 pr_info("dmi-sysfs: Unknown access method %02x\n", 421 - sel->access_method); 427 + sel.access_method); 422 428 return -EIO; 423 429 } 424 430 } ··· 593 595 } 594 596 595 597 /* Set the key */ 596 - entry->dh = *dh; 598 + memcpy(&entry->dh, dh, sizeof(*dh)); 597 599 entry->instance = instance_counts[dh->type]++; 598 600 entry->position = position_count++; 599 601