linux
Failing to preserve invariant causes buffer overflow
Function add_uevent_var() shall guarantee that env->buflen > 0 when it succeeds (i.e. returns 0). However, if !HOTPLUG this function behaves incorrectly, succeeding but not writing anything to env->buf so env->buflen == 0. A subsequent call to input_add_uevent_modalias_var() after the env was initialized executes env->buf[env->buflen - 1] = 'x'. The array index is -1, since env->buflen == 0, and this statement overwrites env->envp_idx which is located right before env->buf in the stack.
Bug fixed by commit 60e233a5660
Type | BufferOverflow |
Config | "!HOTPLUG" (1st degree) |
Fix-in | code |
Location | drivers/ |
#include <errno.h> #include <stdio.h> #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ static char buf[UEVENT_BUFFER_SIZE]; static int buflen = 0; #if defined(CONFIG_HOTPLUG) int add_uevent_var() { int len = sprintf(&buf[buflen], "MODALIAS="); buflen += len + 1; return 0; } #else int add_uevent_var() { return 0; } // (2) #endif int input_add_uevent_modalias_var() { if (add_uevent_var()) return -ENOMEM; buf[buflen - 1] = 'x'; // ERROR: buflen == 0, so buf[-1] is written. return 0; } int main(int argc, char** argv) { input_add_uevent_modalias_var(); // (1) return 0; }
diff --git a/simple/60e233a.c b/simple/60e233a.c --- a/simple/60e233a.c +++ b/simple/60e233a.c @@ -16,7 +16,7 @@ } #else int add_uevent_var() -{ return 0; } +{ return -ENOMEM; } #endif int input_add_uevent_modalias_var()
#include <errno.h> #include <stdio.h> #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ static char buf[UEVENT_BUFFER_SIZE]; static int buflen = 0; int main(int argc, char** argv) { // input_add_uevent_modalias_var(); int r; #if defined(CONFIG_HOTPLUG) int len = sprintf(&buf[buflen], "MODALIAS="); buflen += len + 1; r = 0; #else r = 0; #endif if (r) return -ENOMEM; buf[buflen - 1] = 'x'; // ERROR: buflen == 0, so buf[-1] is written. return 0; }
. call drivers/base/core.c:310:show_uevent() . 336: env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); . 341: retval = kset->uevent_ops->uevent(kset, &dev->kobj, env); .. dyn-call drivers/base/core.c:243:dev_uevent() .. 246: struct device *dev = kobj_to_dev(kobj); .. 294: retval = dev->type->uevent(dev, env); ... dyn-call drivers/input/input.c:1485:input_dev_uevent() ... 1519: INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev); .... call drivers/input/input.c:1446:input_add_uevent_modalias_var() .... 1451: if (add_uevent_var(env, "MODALIAS=")) ...... call include/linux/kobject.h:225:add_uevent_var() ...... 227: return 0; .... ERROR 1454: len = input_print_modalias(&env->buf[env->buflen - 1],