History

modpost: prevent stack buffer overflow in do_input_entry() and do_dmi_entry()

Several functions in scripts/mod/file2alias.c build the module alias string by repeatedly appending into a fixed-size on-stack buffer: char alias[256] = {}; ... sprintf(alias + strlen(alias), "%X,*", i); This pattern is unbounded and silently corrupts the stack when the formatted output exceeds the destination size. Two functions in this file are realistically reachable with input that overflows their buffer: 1. do_input_entry() appends across nine bitmap classes (evbit/keybit/relbit/absbit/mscbit/ledbit/sndbit/ffbit/swbit). The keybit case alone scans bits from INPUT_DEVICE_ID_KEY_MIN_INTERESTING (0x71) to INPUT_DEVICE_ID_KEY_MAX (0x2ff), 655 iterations; if a MODULE_DEVICE_TABLE(input, ...) populates keybit[] densely, the emission reaches ~3132 bytes — overflowing the 256-byte buffer by about 12x. include/linux/mod_devicetable.h declares storage for the full bit range ("keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1]"), so the worst case is reachable per the ABI. 2. do_dmi_entry() emits one ":<prefix>*<filtered_substr>*" segment per matched DMI field, up to 4 matches per dmi_system_id. Each substr is sized as char[79] in struct dmi_strmatch (mod_devicetable.h:584), and dmi_ascii_filter() copies it verbatim into the alias buffer without bounds. Worst case: 4 × (1 + 3 + 1 + 79 + 1) = 336 bytes into alias[256], an 80-byte overflow. No driver in the current tree triggers either case — every in-tree INPUT_DEVICE_ID_MATCH_KEYBIT user populates keybit[] very sparsely (1-3 bits), and no in-tree dmi_system_id has four maximally-long matches. The concern is defense-in-depth: both unbounded sprintf chains are silent stack-corruption primitives in a host build tool, and the buffer sizes have not been revisited since the corresponding code was first introduced. The other do_*_entry() handlers in this file (do_usb_entry, do_cpu_entry, do_typec_entry, ...) were audited and are bounded by their input field sizes (uint16 IDs, fixed-length keys); their alias buffers do not need this treatment. Reproduced under AddressSanitizer with a stand-alone harness mirroring do_input on a fully-populated keybit: ==18319==ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 2 at offset 288 in frame [32, 288) 'alias' #6 do_input poc.c:44 Stack-canary build: Abort trap: 6 (strlen(alias)=3134, cap was 256-1) Add a small alias_append() helper around vsnprintf with a remaining- space check and call fatal() on overflow, matching the modpost style for unrecoverable build conditions. do_input() takes the buffer size as a new parameter; do_input_entry() and do_dmi_entry() pass sizeof(alias) at every call site. dmi_ascii_filter() takes the remaining buffer size as well and aborts on truncation. This bounds every write into the on-stack buffers and turns the latent overflow into a clean build error if it is ever reached.

Hasan Basbunar committed 6 months ago

Commit 49f8fcd1 parent 5200f5f1 file · +53 −26Browse files

Showing the changes to scripts (1 of 1 file). Show all

Unified · Split

scripts/mod/file2alias.c+53−26
@@ -651,7 +651,26 @@ static void do_vio_entry(struct module *mod, void *symval) · 650 unmodified lines
651 module_alias_printf(mod, true, "%s", alias);651 module_alias_printf(mod, true, "%s", alias);
652}652}
653 653
654static void do_input(char *alias,654static void __attribute__((format(printf, 3, 4)))
655alias_append(char *alias, size_t size, const char *fmt, ...)
656{
657 size_t len = strlen(alias);
658 va_list args;
659 int n;
660
661 if (len >= size)
662 fatal("alias buffer (%zu) overflow before append\n", size);
663
664 va_start(args, fmt);
665 n = vsnprintf(alias + len, size - len, fmt, args);
666 va_end(args);
667
668 if (n < 0 || (size_t)n >= size - len)
669 fatal("alias buffer (%zu) overflow on append (need %d, have %zu)\n",
670 size, n, size - len);
671}
672
673static void do_input(char *alias, size_t size,
655 kernel_ulong_t *arr, unsigned int min, unsigned int max)674 kernel_ulong_t *arr, unsigned int min, unsigned int max)
656{675{
657 unsigned int i;676 unsigned int i;
@@ -659,13 +678,14 @@ static void do_input(char *alias, · 1 unmodified line
659 for (i = min; i <= max; i++)678 for (i = min; i <= max; i++)
660 if (get_unaligned_native(arr + i / BITS_PER_LONG) &679 if (get_unaligned_native(arr + i / BITS_PER_LONG) &
661 (1ULL << (i % BITS_PER_LONG)))680 (1ULL << (i % BITS_PER_LONG)))
662 sprintf(alias + strlen(alias), "%X,*", i);681 alias_append(alias, size, "%X,*", i);
663}682}
664 683
665/* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */684/* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */
666static void do_input_entry(struct module *mod, void *symval)685static void do_input_entry(struct module *mod, void *symval)
667{686{
668 char alias[256] = {};687 char alias[256] = {};
688 const size_t sizeof_alias = sizeof(alias);
669 689
670 DEF_FIELD(symval, input_device_id, flags);690 DEF_FIELD(symval, input_device_id, flags);
671 DEF_FIELD(symval, input_device_id, bustype);691 DEF_FIELD(symval, input_device_id, bustype);
@@ -687,35 +707,35 @@ static void do_input_entry(struct module *mod, void *symval) · 15 unmodified lines
687 ADD(alias, "p", flags & INPUT_DEVICE_ID_MATCH_PRODUCT, product);707 ADD(alias, "p", flags & INPUT_DEVICE_ID_MATCH_PRODUCT, product);
688 ADD(alias, "e", flags & INPUT_DEVICE_ID_MATCH_VERSION, version);708 ADD(alias, "e", flags & INPUT_DEVICE_ID_MATCH_VERSION, version);
689 709
690 sprintf(alias + strlen(alias), "-e*");710 alias_append(alias, sizeof_alias, "-e*");
691 if (flags & INPUT_DEVICE_ID_MATCH_EVBIT)711 if (flags & INPUT_DEVICE_ID_MATCH_EVBIT)
692 do_input(alias, *evbit, 0, INPUT_DEVICE_ID_EV_MAX);712 do_input(alias, sizeof_alias, *evbit, 0, INPUT_DEVICE_ID_EV_MAX);
693 sprintf(alias + strlen(alias), "k*");713 alias_append(alias, sizeof_alias, "k*");
694 if (flags & INPUT_DEVICE_ID_MATCH_KEYBIT)714 if (flags & INPUT_DEVICE_ID_MATCH_KEYBIT)
695 do_input(alias, *keybit,715 do_input(alias, sizeof_alias, *keybit,
696 INPUT_DEVICE_ID_KEY_MIN_INTERESTING,716 INPUT_DEVICE_ID_KEY_MIN_INTERESTING,
697 INPUT_DEVICE_ID_KEY_MAX);717 INPUT_DEVICE_ID_KEY_MAX);
698 sprintf(alias + strlen(alias), "r*");718 alias_append(alias, sizeof_alias, "r*");
699 if (flags & INPUT_DEVICE_ID_MATCH_RELBIT)719 if (flags & INPUT_DEVICE_ID_MATCH_RELBIT)
700 do_input(alias, *relbit, 0, INPUT_DEVICE_ID_REL_MAX);720 do_input(alias, sizeof_alias, *relbit, 0, INPUT_DEVICE_ID_REL_MAX);
701 sprintf(alias + strlen(alias), "a*");721 alias_append(alias, sizeof_alias, "a*");
702 if (flags & INPUT_DEVICE_ID_MATCH_ABSBIT)722 if (flags & INPUT_DEVICE_ID_MATCH_ABSBIT)
703 do_input(alias, *absbit, 0, INPUT_DEVICE_ID_ABS_MAX);723 do_input(alias, sizeof_alias, *absbit, 0, INPUT_DEVICE_ID_ABS_MAX);
704 sprintf(alias + strlen(alias), "m*");724 alias_append(alias, sizeof_alias, "m*");
705 if (flags & INPUT_DEVICE_ID_MATCH_MSCIT)725 if (flags & INPUT_DEVICE_ID_MATCH_MSCIT)
706 do_input(alias, *mscbit, 0, INPUT_DEVICE_ID_MSC_MAX);726 do_input(alias, sizeof_alias, *mscbit, 0, INPUT_DEVICE_ID_MSC_MAX);
707 sprintf(alias + strlen(alias), "l*");727 alias_append(alias, sizeof_alias, "l*");
708 if (flags & INPUT_DEVICE_ID_MATCH_LEDBIT)728 if (flags & INPUT_DEVICE_ID_MATCH_LEDBIT)
709 do_input(alias, *ledbit, 0, INPUT_DEVICE_ID_LED_MAX);729 do_input(alias, sizeof_alias, *ledbit, 0, INPUT_DEVICE_ID_LED_MAX);
710 sprintf(alias + strlen(alias), "s*");730 alias_append(alias, sizeof_alias, "s*");
711 if (flags & INPUT_DEVICE_ID_MATCH_SNDBIT)731 if (flags & INPUT_DEVICE_ID_MATCH_SNDBIT)
712 do_input(alias, *sndbit, 0, INPUT_DEVICE_ID_SND_MAX);732 do_input(alias, sizeof_alias, *sndbit, 0, INPUT_DEVICE_ID_SND_MAX);
713 sprintf(alias + strlen(alias), "f*");733 alias_append(alias, sizeof_alias, "f*");
714 if (flags & INPUT_DEVICE_ID_MATCH_FFBIT)734 if (flags & INPUT_DEVICE_ID_MATCH_FFBIT)
715 do_input(alias, *ffbit, 0, INPUT_DEVICE_ID_FF_MAX);735 do_input(alias, sizeof_alias, *ffbit, 0, INPUT_DEVICE_ID_FF_MAX);
716 sprintf(alias + strlen(alias), "w*");736 alias_append(alias, sizeof_alias, "w*");
717 if (flags & INPUT_DEVICE_ID_MATCH_SWBIT)737 if (flags & INPUT_DEVICE_ID_MATCH_SWBIT)
718 do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX);738 do_input(alias, sizeof_alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX);
719 739
720 module_alias_printf(mod, false, "input:%s", alias);740 module_alias_printf(mod, false, "input:%s", alias);
721}741}
@@ -895,12 +915,16 @@ static const struct dmifield { · 173 unmodified lines
895 { NULL, DMI_NONE }915 { NULL, DMI_NONE }
896};916};
897 917
898static void dmi_ascii_filter(char *d, const char *s)918static void dmi_ascii_filter(char *d, size_t avail, const char *s)
899{919{
900 /* Filter out characters we don't want to see in the modalias string */920 /* Filter out characters we don't want to see in the modalias string */
901 for (; *s; s++)921 for (; *s; s++)
902 if (*s > ' ' && *s < 127 && *s != ':')922 if (*s > ' ' && *s < 127 && *s != ':') {
923 if (avail <= 1)
924 fatal("%s: alias buffer overflow\n", __func__);
903 *(d++) = *s;925 *(d++) = *s;
926 avail--;
927 }
904 928
905 *d = 0;929 *d = 0;
906}930}
@@ -909,6 +933,8 @@ static void dmi_ascii_filter(char *d, const char *s) · 2 unmodified lines
909static void do_dmi_entry(struct module *mod, void *symval)933static void do_dmi_entry(struct module *mod, void *symval)
910{934{
911 char alias[256] = {};935 char alias[256] = {};
936 const size_t sizeof_alias = sizeof(alias);
937 size_t len;
912 int i, j;938 int i, j;
913 DEF_FIELD_ADDR(symval, dmi_system_id, matches);939 DEF_FIELD_ADDR(symval, dmi_system_id, matches);
914 940
@@ -916,11 +942,12 @@ static void do_dmi_entry(struct module *mod, void *symval) · 1 unmodified line
916 for (j = 0; j < 4; j++) {942 for (j = 0; j < 4; j++) {
917 if ((*matches)[j].slot &&943 if ((*matches)[j].slot &&
918 (*matches)[j].slot == dmi_fields[i].field) {944 (*matches)[j].slot == dmi_fields[i].field) {
919 sprintf(alias + strlen(alias), ":%s*",945 alias_append(alias, sizeof_alias, ":%s*",
920 dmi_fields[i].prefix);946 dmi_fields[i].prefix);
921 dmi_ascii_filter(alias + strlen(alias),947 len = strlen(alias);
948 dmi_ascii_filter(alias + len, sizeof_alias - len,
922 (*matches)[j].substr);949 (*matches)[j].substr);
923 strcat(alias, "*");950 alias_append(alias, sizeof_alias, "*");
924 }951 }
925 }952 }
926 }953 }