diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-01 02:00:29 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-01 02:00:29 +0100 |
commit | de9e007d9105bf8fa613a89810feff32a43add03 (patch) | |
tree | ef7805d1e03e8648fbaca3713d1a749c27770339 | |
parent | Merge master.kernel.org:/home/rmk/linux-2.6-serial (diff) | |
download | linux-de9e007d9105bf8fa613a89810feff32a43add03.tar.xz linux-de9e007d9105bf8fa613a89810feff32a43add03.zip |
sysctl: make sure to terminate strings with a NUL
This is a slightly more complete fix for the previous minimal sysctl
string fix. It always terminates the returned string with a NUL, even
if the full result wouldn't fit in the user-supplied buffer.
The returned length is the full untruncated length, so that you can
tell when truncation has occurred.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | kernel/sysctl.c | 25 |
1 files changed, 15 insertions, 10 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e5102ea6e104..b53115b882e1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2192,27 +2192,32 @@ int sysctl_string(ctl_table *table, int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, void __user *newval, size_t newlen, void **context) { - size_t l, len; - if (!table->data || !table->maxlen) return -ENOTDIR; if (oldval && oldlenp) { - if (get_user(len, oldlenp)) + size_t bufsize; + if (get_user(bufsize, oldlenp)) return -EFAULT; - if (len) { - l = strlen(table->data)+1; - if (len > l) len = l; - if (len >= table->maxlen) + if (bufsize) { + size_t len = strlen(table->data), copied; + + /* This shouldn't trigger for a well-formed sysctl */ + if (len > table->maxlen) len = table->maxlen; - if(copy_to_user(oldval, table->data, len)) + + /* Copy up to a max of bufsize-1 bytes of the string */ + copied = (len >= bufsize) ? bufsize - 1 : len; + + if (copy_to_user(oldval, table->data, copied) || + put_user(0, (char __user *)(oldval + copied))) return -EFAULT; - if(put_user(len, oldlenp)) + if (put_user(len, oldlenp)) return -EFAULT; } } if (newval && newlen) { - len = newlen; + size_t len = newlen; if (len > table->maxlen) len = table->maxlen; if(copy_from_user(table->data, newval, len)) |