Discussion:
Bug#772807: binfmt-support: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Colin Watson
2014-12-11 10:44:09 UTC
Permalink
upgrading kernel to current Linus' current git (11 December 2014)
Thu Dec 11 20:40:29 2014: [....] Enabling additional executable binary formats: binfmt-supportupdate-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: exiting due to previous errors
The latest binfmt_misc module in git has much more detailed debugging
output in dmesg. What does "dmesg | grep binfmt_misc" say?

Thanks,
--
Colin Watson [***@debian.org]
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Arthur Marsh
2014-12-11 12:02:00 UTC
Permalink
Post by Colin Watson
upgrading kernel to current Linus' current git (11 December 2014)
Thu Dec 11 20:40:29 2014: [....] Enabling additional executable binary formats: binfmt-supportupdate-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: exiting due to previous errors
The latest binfmt_misc module in git has much more detailed debugging
output in dmesg. What does "dmesg | grep binfmt_misc" say?
Thanks,
Hi, I'm seeing:

$ dmesg|grep binfmt_misc
$ lsmod|grep binfmt_misc
binfmt_misc 17691 1
$ su
Password:
# modinfo binfmt_misc
filename: /lib/modules/3.18.0+/kernel/fs/binfmt_misc.ko
license: GPL
alias: fs-binfmt_misc
depends:
intree: Y
vermagic: 3.18.0+ SMP preempt mod_unload modversions K8

Regards,

Arthur.
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Colin Watson
2014-12-11 12:40:24 UTC
Permalink
Post by Arthur Marsh
Post by Colin Watson
The latest binfmt_misc module in git has much more detailed debugging
output in dmesg. What does "dmesg | grep binfmt_misc" say?
$ dmesg|grep binfmt_misc
Hm. Does your tree include
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/fs/binfmt_misc.c?id=6b899c4e9a049dfca759d990bd53b14f81c3626c
? If not, it would help to try again with that.

(Hm, I guess you might need CONFIG_DYNAMIC_DEBUG. Not sure.)

Thanks,
--
Colin Watson [***@debian.org]
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Arthur Marsh
2014-12-11 19:11:40 UTC
Permalink
Post by Colin Watson
Post by Arthur Marsh
Post by Colin Watson
The latest binfmt_misc module in git has much more detailed debugging
output in dmesg. What does "dmesg | grep binfmt_misc" say?
$ dmesg|grep binfmt_misc
Hm. Does your tree include
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/fs/binfmt_misc.c?id=6b899c4e9a049dfca759d990bd53b14f81c3626c
? If not, it would help to try again with that.
(Hm, I guess you might need CONFIG_DYNAMIC_DEBUG. Not sure.)
Thanks,
I don't have time to git-bisect the kernel now, but reverting to 3.18.0
removed the problem.

CONFIG_DYNAMIC_DEBUG was already enabled, and that commit was already
included.

So it might be worth running a git-bisect just on fs/binfmt_misc.c to
see if one of the recent commits changed behaviour unexpectedly.

Regards,

Arthur.
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Arthur Marsh
2014-12-12 04:21:55 UTC
Permalink
Post by Colin Watson
Post by Arthur Marsh
Post by Colin Watson
The latest binfmt_misc module in git has much more detailed debugging
output in dmesg. What does "dmesg | grep binfmt_misc" say?
$ dmesg|grep binfmt_misc
Hm. Does your tree include
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/fs/binfmt_misc.c?id=6b899c4e9a049dfca759d990bd53b14f81c3626c
? If not, it would help to try again with that.
(Hm, I guess you might need CONFIG_DYNAMIC_DEBUG. Not sure.)
Thanks,
The earlier conversation is at:

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772807

Short version, on recent kernels I was seeing:

Thu Dec 11 20:40:29 2014: [....] Enabling additional executable binary
formats:
binfmt-supportupdate-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument
Thu Dec 11 20:40:30 2014: update-binfmts: warning: unable to close
/proc/sys/fs/binfmt_misc/register: Invalid argument

and only the first of several binfmt's registered (all the qemu
binfmt's) when update-binfmts was run at boot time.

A git-bisect revealed:

git bisect good
6b899c4e9a049dfca759d990bd53b14f81c3626c is the first bad commit
commit 6b899c4e9a049dfca759d990bd53b14f81c3626c
Author: Mike Frysinger <***@gentoo.org>
Date: Wed Dec 10 15:52:08 2014 -0800

binfmt_misc: add comments & debug logs

When trying to develop a custom format handler, the errors returned all
effectively get bucketed as EINVAL with no kernel messages. The other
errors (ENOMEM/EFAULT) are internal/obvious and basic. Thus any time a
bad handler is rejected, the developer has to walk the dense code and
try to guess where it went wrong. Needing to dive into kernel code is
itself a fairly high barrier for a lot of people.

To improve this situation, let's deploy extensive pr_debug markers at
logical parse points, and add comments to the dense parsing logic. It
let's you see exactly where the parsing aborts, the string the kernel
received (useful when dealing with shell code), how it translated the
buffers to binary data, and how it will apply the mask at runtime.

Some example output:
$ echo
':qemu-foo:M::\x7fELF\xAD\xAD\x01\x00:\xff\xff\xff\xff\xff\x00\xff\x00:/usr/bin/qemu-foo:POC'
Post by Colin Watson
register
$ dmesg
binfmt_misc: register: received 92 bytes
binfmt_misc: register: delim: 0x3a {:}
binfmt_misc: register: name: {qemu-foo}
binfmt_misc: register: type: M (magic)
binfmt_misc: register: offset: 0x0
binfmt_misc: register: magic[raw]: 5c 78 37 66 45 4c 46 5c 78 41
44 5c 78 41 44 5c \x7fELF\xAD\xAD\
binfmt_misc: register: magic[raw]: 78 30 31 5c 78 30 30 00
x01\x00.
binfmt_misc: register: mask[raw]: 5c 78 66 66 5c 78 66 66 5c 78
66 66 5c 78 66 66 \xff\xff\xff\xff
binfmt_misc: register: mask[raw]: 5c 78 66 66 5c 78 30 30 5c 78
66 66 5c 78 30 30 \xff\x00\xff\x00
binfmt_misc: register: mask[raw]: 00
.
binfmt_misc: register: magic/mask length: 8
binfmt_misc: register: magic[decoded]: 7f 45 4c 46 ad ad 01 00
.ELF....
binfmt_misc: register: mask[decoded]: ff ff ff ff ff 00 ff 00
........
binfmt_misc: register: magic[masked]: 7f 45 4c 46 ad 00 01 00
.ELF....
binfmt_misc: register: interpreter: {/usr/bin/qemu-foo}
binfmt_misc: register: flag: P (preserve argv0)
binfmt_misc: register: flag: O (open binary)
binfmt_misc: register: flag: C (preserve creds)

The [raw] lines show us exactly what was received from userspace. The
lines after that show us how the kernel has decoded things.

Signed-off-by: Mike Frysinger <***@gentoo.org>
Cc: Al Viro <***@zeniv.linux.org.uk>
Cc: Joe Perches <***@perches.com>
Signed-off-by: Andrew Morton <***@linux-foundation.org>
Signed-off-by: Linus Torvalds <***@linux-foundation.org>

:040000 040000 d8354a4a420ed15399a6c41aa0914a8a4c6dba9a
2d491c10c9418cd16f367916f25d6050eb60152d M fs

Regards,

Arthur.
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Al Viro
2014-12-12 06:01:38 UTC
Permalink
Post by Arthur Marsh
6b899c4e9a049dfca759d990bd53b14f81c3626c is the first bad commit
commit 6b899c4e9a049dfca759d990bd53b14f81c3626c
Date: Wed Dec 10 15:52:08 2014 -0800
binfmt_misc: add comments & debug logs
When trying to develop a custom format handler, the errors returned all
effectively get bucketed as EINVAL with no kernel messages. The other
errors (ENOMEM/EFAULT) are internal/obvious and basic. Thus any time a
bad handler is rejected, the developer has to walk the dense code and
try to guess where it went wrong. Needing to dive into kernel code is
itself a fairly high barrier for a lot of people.
To improve this situation, let's deploy extensive pr_debug markers at
logical parse points, and add comments to the dense parsing logic. It
let's you see exactly where the parsing aborts, the string the kernel
received (useful when dealing with shell code), how it translated the
buffers to binary data, and how it will apply the mask at runtime.
... and looking at it shows the following garbage:
p[-1] = '\0';
- if (!e->magic[0])
+ if (p == e->magic)

That code makes no sense whatsoever - if p *was* equal to e->magic, the
assignment before it would be obviously broken. And a few lines later we
have another chunk just like that.

Moreover, if you look at further context, you'll see that it's
e->magic = p;
p = scanarg(p, del);
if (!p)
sod off
p[-1] = '\0';
if (p == e->magic)
sod off
Now, that condition could be true only if scanarg(p, del) would return p,
right? Let's take a look at scanarg():
static char *scanarg(char *s, char del)
{
char c;

while ((c = *s++) != del) {
if (c == '\\' && *s == 'x') {
s++;
if (!isxdigit(*s++))
return NULL;
if (!isxdigit(*s++))
return NULL;
}
}
return s;
}

See that s++ in the loop condition? There's no way in hell it would *not*
increment s. If we return non-NULL, we know that c was equal to del *and*
c is equal to previous_value_of_s[0], i.e. s[-1]. IOW, return value points
to the byte following the first instance of delimeter starting at the argument.

And p[-1] = '\0' replaces delimiter with NUL. IOW, the checks used to be
correct. And got buggered.

Subject: unfuck fs/binfmt_misc.c

Undo some of the "prettifying" braindamage.

Signed-off-by: Al Viro <***@zeniv.linux.org.uk>
---
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 70789e1..a6b6697 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -5,6 +5,8 @@
*
* binfmt_misc detects binaries via a magic or filename extension and invokes
* a specified wrapper. See Documentation/binfmt_misc.txt for more details.
+ *
+ * Cetero censeo, checkpatch.pl esse delendam
*/

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -131,9 +133,8 @@ static int load_misc_binary(struct linux_binprm *bprm)
int retval;
int fd_binary = -1;

- retval = -ENOEXEC;
if (!enabled)
- goto ret;
+ return -ENOEXEC;

/* to keep locking time low, we copy the interpreter string */
read_lock(&entries_lock);
@@ -142,12 +143,12 @@ static int load_misc_binary(struct linux_binprm *bprm)
strlcpy(iname, fmt->interpreter, BINPRM_BUF_SIZE);
read_unlock(&entries_lock);
if (!fmt)
- goto ret;
+ return -ENOEXEC;

if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
retval = remove_arg_zero(bprm);
if (retval)
- goto ret;
+ return retval;
}

if (fmt->flags & MISC_FMT_OPEN_BINARY) {
@@ -157,10 +158,9 @@ static int load_misc_binary(struct linux_binprm *bprm)
* to it
*/
fd_binary = get_unused_fd_flags(0);
- if (fd_binary < 0) {
- retval = fd_binary;
- goto ret;
- }
+ if (fd_binary < 0)
+ return fd_binary;
+
fd_install(fd_binary, bprm->file);

/* if the binary is not readable than enforce mm->dumpable=0
@@ -219,14 +219,13 @@ static int load_misc_binary(struct linux_binprm *bprm)
if (retval < 0)
goto error;

-ret:
return retval;
error:
if (fd_binary > 0)
sys_close(fd_binary);
bprm->interp_flags = 0;
bprm->interp_data = 0;
- goto ret;
+ return retval;
}

/* Command parsers */
@@ -250,6 +249,7 @@ static char *scanarg(char *s, char del)
return NULL;
}
}
+ s[-1] = '\0';
return s;
}

@@ -316,7 +316,7 @@ static Node *create_entry(const char __user *buffer, size_t count)

memset(e, 0, sizeof(Node));
if (copy_from_user(buf, buffer, count))
- goto efault;
+ goto Efault;

del = *p++; /* delimeter */

@@ -329,13 +329,13 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->name = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->name[0] ||
!strcmp(e->name, ".") ||
!strcmp(e->name, "..") ||
strchr(e->name, '/'))
- goto einval;
+ goto Einval;

pr_debug("register: name: {%s}\n", e->name);

@@ -350,10 +350,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->flags = (1 << Enabled) | (1 << Magic);
break;
default:
- goto einval;
+ goto Einval;
}
if (*p++ != del)
- goto einval;
+ goto Einval;

if (test_bit(Magic, &e->flags)) {
/* Handle the 'M' (magic) format. */
@@ -362,21 +362,20 @@ static Node *create_entry(const char __user *buffer, size_t count)
/* Parse the 'offset' field. */
s = strchr(p, del);
if (!s)
- goto einval;
+ goto Einval;
*s++ = '\0';
e->offset = simple_strtoul(p, &p, 10);
if (*p++)
- goto einval;
+ goto Einval;
pr_debug("register: offset: %#x\n", e->offset);

/* Parse the 'magic' field. */
e->magic = p;
p = scanarg(p, del);
if (!p)
- goto einval;
- p[-1] = '\0';
- if (p == e->magic)
- goto einval;
+ goto Einval;
+ if (!e->magic[0])
+ goto Einval;
if (USE_DEBUG)
print_hex_dump_bytes(
KBUILD_MODNAME ": register: magic[raw]: ",
@@ -386,9 +385,8 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->mask = p;
p = scanarg(p, del);
if (!p)
- goto einval;
- p[-1] = '\0';
- if (p == e->mask) {
+ goto Einval;
+ if (!e->mask[0]) {
e->mask = NULL;
pr_debug("register: mask[raw]: none\n");
} else if (USE_DEBUG)
@@ -405,9 +403,9 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX);
if (e->mask &&
string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size)
- goto einval;
+ goto Einval;
if (e->size + e->offset > BINPRM_BUF_SIZE)
- goto einval;
+ goto Einval;
pr_debug("register: magic/mask length: %i\n", e->size);
if (USE_DEBUG) {
print_hex_dump_bytes(
@@ -439,23 +437,23 @@ static Node *create_entry(const char __user *buffer, size_t count)
/* Skip the 'offset' field. */
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';

/* Parse the 'magic' field. */
e->magic = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->magic[0] || strchr(e->magic, '/'))
- goto einval;
+ goto Einval;
pr_debug("register: extension: {%s}\n", e->magic);

/* Skip the 'mask' field. */
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
}

@@ -463,10 +461,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->interpreter = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->interpreter[0])
- goto einval;
+ goto Einval;
pr_debug("register: interpreter: {%s}\n", e->interpreter);

/* Parse the 'flags' field. */
@@ -474,17 +472,17 @@ static Node *create_entry(const char __user *buffer, size_t count)
if (*p == '\n')
p++;
if (p != buf + count)
- goto einval;
+ goto Einval;

return e;

out:
return ERR_PTR(err);

-efault:
+Efault:
kfree(e);
return ERR_PTR(-EFAULT);
-einval:
+Einval:
kfree(e);
return ERR_PTR(-EINVAL);
}
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Arthur Marsh
2014-12-12 13:10:01 UTC
Permalink
Post by Al Viro
Post by Arthur Marsh
6b899c4e9a049dfca759d990bd53b14f81c3626c is the first bad commit
commit 6b899c4e9a049dfca759d990bd53b14f81c3626c
Date: Wed Dec 10 15:52:08 2014 -0800
binfmt_misc: add comments & debug logs
When trying to develop a custom format handler, the errors returned all
effectively get bucketed as EINVAL with no kernel messages. The other
errors (ENOMEM/EFAULT) are internal/obvious and basic. Thus any time a
bad handler is rejected, the developer has to walk the dense code and
try to guess where it went wrong. Needing to dive into kernel code is
itself a fairly high barrier for a lot of people.
To improve this situation, let's deploy extensive pr_debug markers at
logical parse points, and add comments to the dense parsing logic. It
let's you see exactly where the parsing aborts, the string the kernel
received (useful when dealing with shell code), how it translated the
buffers to binary data, and how it will apply the mask at runtime.
p[-1] = '\0';
- if (!e->magic[0])
+ if (p == e->magic)
That code makes no sense whatsoever - if p *was* equal to e->magic, the
assignment before it would be obviously broken. And a few lines later we
have another chunk just like that.
Moreover, if you look at further context, you'll see that it's
e->magic = p;
p = scanarg(p, del);
if (!p)
sod off
p[-1] = '\0';
if (p == e->magic)
sod off
Now, that condition could be true only if scanarg(p, del) would return p,
static char *scanarg(char *s, char del)
{
char c;
while ((c = *s++) != del) {
if (c == '\\' && *s == 'x') {
s++;
if (!isxdigit(*s++))
return NULL;
if (!isxdigit(*s++))
return NULL;
}
}
return s;
}
See that s++ in the loop condition? There's no way in hell it would *not*
increment s. If we return non-NULL, we know that c was equal to del *and*
c is equal to previous_value_of_s[0], i.e. s[-1]. IOW, return value points
to the byte following the first instance of delimeter starting at the argument.
And p[-1] = '\0' replaces delimiter with NUL. IOW, the checks used to be
correct. And got buggered.
Subject: unfuck fs/binfmt_misc.c
Undo some of the "prettifying" braindamage.
---
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 70789e1..a6b6697 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -5,6 +5,8 @@
*
* binfmt_misc detects binaries via a magic or filename extension and invokes
* a specified wrapper. See Documentation/binfmt_misc.txt for more details.
+ *
+ * Cetero censeo, checkpatch.pl esse delendam
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -131,9 +133,8 @@ static int load_misc_binary(struct linux_binprm *bprm)
int retval;
int fd_binary = -1;
- retval = -ENOEXEC;
if (!enabled)
- goto ret;
+ return -ENOEXEC;
/* to keep locking time low, we copy the interpreter string */
read_lock(&entries_lock);
@@ -142,12 +143,12 @@ static int load_misc_binary(struct linux_binprm *bprm)
strlcpy(iname, fmt->interpreter, BINPRM_BUF_SIZE);
read_unlock(&entries_lock);
if (!fmt)
- goto ret;
+ return -ENOEXEC;
if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
retval = remove_arg_zero(bprm);
if (retval)
- goto ret;
+ return retval;
}
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
@@ -157,10 +158,9 @@ static int load_misc_binary(struct linux_binprm *bprm)
* to it
*/
fd_binary = get_unused_fd_flags(0);
- if (fd_binary < 0) {
- retval = fd_binary;
- goto ret;
- }
+ if (fd_binary < 0)
+ return fd_binary;
+
fd_install(fd_binary, bprm->file);
/* if the binary is not readable than enforce mm->dumpable=0
@@ -219,14 +219,13 @@ static int load_misc_binary(struct linux_binprm *bprm)
if (retval < 0)
goto error;
return retval;
if (fd_binary > 0)
sys_close(fd_binary);
bprm->interp_flags = 0;
bprm->interp_data = 0;
- goto ret;
+ return retval;
}
/* Command parsers */
@@ -250,6 +249,7 @@ static char *scanarg(char *s, char del)
return NULL;
}
}
+ s[-1] = '\0';
return s;
}
@@ -316,7 +316,7 @@ static Node *create_entry(const char __user *buffer, size_t count)
memset(e, 0, sizeof(Node));
if (copy_from_user(buf, buffer, count))
- goto efault;
+ goto Efault;
del = *p++; /* delimeter */
@@ -329,13 +329,13 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->name = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->name[0] ||
!strcmp(e->name, ".") ||
!strcmp(e->name, "..") ||
strchr(e->name, '/'))
- goto einval;
+ goto Einval;
pr_debug("register: name: {%s}\n", e->name);
@@ -350,10 +350,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->flags = (1 << Enabled) | (1 << Magic);
break;
- goto einval;
+ goto Einval;
}
if (*p++ != del)
- goto einval;
+ goto Einval;
if (test_bit(Magic, &e->flags)) {
/* Handle the 'M' (magic) format. */
@@ -362,21 +362,20 @@ static Node *create_entry(const char __user *buffer, size_t count)
/* Parse the 'offset' field. */
s = strchr(p, del);
if (!s)
- goto einval;
+ goto Einval;
*s++ = '\0';
e->offset = simple_strtoul(p, &p, 10);
if (*p++)
- goto einval;
+ goto Einval;
pr_debug("register: offset: %#x\n", e->offset);
/* Parse the 'magic' field. */
e->magic = p;
p = scanarg(p, del);
if (!p)
- goto einval;
- p[-1] = '\0';
- if (p == e->magic)
- goto einval;
+ goto Einval;
+ if (!e->magic[0])
+ goto Einval;
if (USE_DEBUG)
print_hex_dump_bytes(
KBUILD_MODNAME ": register: magic[raw]: ",
@@ -386,9 +385,8 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->mask = p;
p = scanarg(p, del);
if (!p)
- goto einval;
- p[-1] = '\0';
- if (p == e->mask) {
+ goto Einval;
+ if (!e->mask[0]) {
e->mask = NULL;
pr_debug("register: mask[raw]: none\n");
} else if (USE_DEBUG)
@@ -405,9 +403,9 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX);
if (e->mask &&
string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size)
- goto einval;
+ goto Einval;
if (e->size + e->offset > BINPRM_BUF_SIZE)
- goto einval;
+ goto Einval;
pr_debug("register: magic/mask length: %i\n", e->size);
if (USE_DEBUG) {
print_hex_dump_bytes(
@@ -439,23 +437,23 @@ static Node *create_entry(const char __user *buffer, size_t count)
/* Skip the 'offset' field. */
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
/* Parse the 'magic' field. */
e->magic = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->magic[0] || strchr(e->magic, '/'))
- goto einval;
+ goto Einval;
pr_debug("register: extension: {%s}\n", e->magic);
/* Skip the 'mask' field. */
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
}
@@ -463,10 +461,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->interpreter = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->interpreter[0])
- goto einval;
+ goto Einval;
pr_debug("register: interpreter: {%s}\n", e->interpreter);
/* Parse the 'flags' field. */
@@ -474,17 +472,17 @@ static Node *create_entry(const char __user *buffer, size_t count)
if (*p == '\n')
p++;
if (p != buf + count)
- goto einval;
+ goto Einval;
return e;
return ERR_PTR(err);
kfree(e);
return ERR_PTR(-EFAULT);
kfree(e);
return ERR_PTR(-EINVAL);
}
Thanks, I've successfully applied and built the patch and update-binfmts
works again.

Arthur.
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Arthur Marsh
2014-12-14 08:33:57 UTC
Permalink
Post by Al Viro
Post by Arthur Marsh
6b899c4e9a049dfca759d990bd53b14f81c3626c is the first bad commit
commit 6b899c4e9a049dfca759d990bd53b14f81c3626c
Date: Wed Dec 10 15:52:08 2014 -0800
binfmt_misc: add comments & debug logs
When trying to develop a custom format handler, the errors returned all
effectively get bucketed as EINVAL with no kernel messages. The other
errors (ENOMEM/EFAULT) are internal/obvious and basic. Thus any time a
bad handler is rejected, the developer has to walk the dense code and
try to guess where it went wrong. Needing to dive into kernel code is
itself a fairly high barrier for a lot of people.
To improve this situation, let's deploy extensive pr_debug markers at
logical parse points, and add comments to the dense parsing logic. It
let's you see exactly where the parsing aborts, the string the kernel
received (useful when dealing with shell code), how it translated the
buffers to binary data, and how it will apply the mask at runtime.
p[-1] = '\0';
- if (!e->magic[0])
+ if (p == e->magic)
That code makes no sense whatsoever - if p *was* equal to e->magic, the
assignment before it would be obviously broken. And a few lines later we
have another chunk just like that.
Moreover, if you look at further context, you'll see that it's
e->magic = p;
p = scanarg(p, del);
if (!p)
sod off
p[-1] = '\0';
if (p == e->magic)
sod off
Now, that condition could be true only if scanarg(p, del) would return p,
static char *scanarg(char *s, char del)
{
char c;
while ((c = *s++) != del) {
if (c == '\\' && *s == 'x') {
s++;
if (!isxdigit(*s++))
return NULL;
if (!isxdigit(*s++))
return NULL;
}
}
return s;
}
See that s++ in the loop condition? There's no way in hell it would *not*
increment s. If we return non-NULL, we know that c was equal to del *and*
c is equal to previous_value_of_s[0], i.e. s[-1]. IOW, return value points
to the byte following the first instance of delimeter starting at the argument.
And p[-1] = '\0' replaces delimiter with NUL. IOW, the checks used to be
correct. And got buggered.
Subject: unfuck fs/binfmt_misc.c
Undo some of the "prettifying" braindamage.
---
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 70789e1..a6b6697 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -5,6 +5,8 @@
*
* binfmt_misc detects binaries via a magic or filename extension and invokes
* a specified wrapper. See Documentation/binfmt_misc.txt for more details.
+ *
+ * Cetero censeo, checkpatch.pl esse delendam
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -131,9 +133,8 @@ static int load_misc_binary(struct linux_binprm *bprm)
int retval;
int fd_binary = -1;
- retval = -ENOEXEC;
if (!enabled)
- goto ret;
+ return -ENOEXEC;
/* to keep locking time low, we copy the interpreter string */
read_lock(&entries_lock);
@@ -142,12 +143,12 @@ static int load_misc_binary(struct linux_binprm *bprm)
strlcpy(iname, fmt->interpreter, BINPRM_BUF_SIZE);
read_unlock(&entries_lock);
if (!fmt)
- goto ret;
+ return -ENOEXEC;
if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
retval = remove_arg_zero(bprm);
if (retval)
- goto ret;
+ return retval;
}
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
@@ -157,10 +158,9 @@ static int load_misc_binary(struct linux_binprm *bprm)
* to it
*/
fd_binary = get_unused_fd_flags(0);
- if (fd_binary < 0) {
- retval = fd_binary;
- goto ret;
- }
+ if (fd_binary < 0)
+ return fd_binary;
+
fd_install(fd_binary, bprm->file);
/* if the binary is not readable than enforce mm->dumpable=0
@@ -219,14 +219,13 @@ static int load_misc_binary(struct linux_binprm *bprm)
if (retval < 0)
goto error;
return retval;
if (fd_binary > 0)
sys_close(fd_binary);
bprm->interp_flags = 0;
bprm->interp_data = 0;
- goto ret;
+ return retval;
}
/* Command parsers */
@@ -250,6 +249,7 @@ static char *scanarg(char *s, char del)
return NULL;
}
}
+ s[-1] = '\0';
return s;
}
@@ -316,7 +316,7 @@ static Node *create_entry(const char __user *buffer, size_t count)
memset(e, 0, sizeof(Node));
if (copy_from_user(buf, buffer, count))
- goto efault;
+ goto Efault;
del = *p++; /* delimeter */
@@ -329,13 +329,13 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->name = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->name[0] ||
!strcmp(e->name, ".") ||
!strcmp(e->name, "..") ||
strchr(e->name, '/'))
- goto einval;
+ goto Einval;
pr_debug("register: name: {%s}\n", e->name);
@@ -350,10 +350,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->flags = (1 << Enabled) | (1 << Magic);
break;
- goto einval;
+ goto Einval;
}
if (*p++ != del)
- goto einval;
+ goto Einval;
if (test_bit(Magic, &e->flags)) {
/* Handle the 'M' (magic) format. */
@@ -362,21 +362,20 @@ static Node *create_entry(const char __user *buffer, size_t count)
/* Parse the 'offset' field. */
s = strchr(p, del);
if (!s)
- goto einval;
+ goto Einval;
*s++ = '\0';
e->offset = simple_strtoul(p, &p, 10);
if (*p++)
- goto einval;
+ goto Einval;
pr_debug("register: offset: %#x\n", e->offset);
/* Parse the 'magic' field. */
e->magic = p;
p = scanarg(p, del);
if (!p)
- goto einval;
- p[-1] = '\0';
- if (p == e->magic)
- goto einval;
+ goto Einval;
+ if (!e->magic[0])
+ goto Einval;
if (USE_DEBUG)
print_hex_dump_bytes(
KBUILD_MODNAME ": register: magic[raw]: ",
@@ -386,9 +385,8 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->mask = p;
p = scanarg(p, del);
if (!p)
- goto einval;
- p[-1] = '\0';
- if (p == e->mask) {
+ goto Einval;
+ if (!e->mask[0]) {
e->mask = NULL;
pr_debug("register: mask[raw]: none\n");
} else if (USE_DEBUG)
@@ -405,9 +403,9 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX);
if (e->mask &&
string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size)
- goto einval;
+ goto Einval;
if (e->size + e->offset > BINPRM_BUF_SIZE)
- goto einval;
+ goto Einval;
pr_debug("register: magic/mask length: %i\n", e->size);
if (USE_DEBUG) {
print_hex_dump_bytes(
@@ -439,23 +437,23 @@ static Node *create_entry(const char __user *buffer, size_t count)
/* Skip the 'offset' field. */
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
/* Parse the 'magic' field. */
e->magic = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->magic[0] || strchr(e->magic, '/'))
- goto einval;
+ goto Einval;
pr_debug("register: extension: {%s}\n", e->magic);
/* Skip the 'mask' field. */
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
}
@@ -463,10 +461,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
e->interpreter = p;
p = strchr(p, del);
if (!p)
- goto einval;
+ goto Einval;
*p++ = '\0';
if (!e->interpreter[0])
- goto einval;
+ goto Einval;
pr_debug("register: interpreter: {%s}\n", e->interpreter);
/* Parse the 'flags' field. */
@@ -474,17 +472,17 @@ static Node *create_entry(const char __user *buffer, size_t count)
if (*p == '\n')
p++;
if (p != buf + count)
- goto einval;
+ goto Einval;
return e;
return ERR_PTR(err);
kfree(e);
return ERR_PTR(-EFAULT);
kfree(e);
return ERR_PTR(-EINVAL);
}
I had to revert Al Viro's "Undo some of the "prettifying" braindamage."
patch above to apply the execveat() commit below that is now in Linus'
git head:

https://github.com/torvalds/linux/commit/51f39a1f0cea1cacf8c787f652f26dfee9611874

After that, trying to apply Al Viro's patch resulted in on failed hunk
and code that wouldn't build.

binfmt_misc.c building but not working doesn't break things for me, but
it would be good to get fixed.

Regards,

Arthur.
--
To UNSUBSCRIBE, email to debian-bugs-dist-***@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact ***@lists.debian.org
Mike Frysinger
2014-12-31 06:23:03 UTC
Permalink
Post by Al Viro
Post by Arthur Marsh
6b899c4e9a049dfca759d990bd53b14f81c3626c is the first bad commit
commit 6b899c4e9a049dfca759d990bd53b14f81c3626c
Date: Wed Dec 10 15:52:08 2014 -0800
binfmt_misc: add comments & debug logs
When trying to develop a custom format handler, the errors returned all
effectively get bucketed as EINVAL with no kernel messages. The other
errors (ENOMEM/EFAULT) are internal/obvious and basic. Thus any time a
bad handler is rejected, the developer has to walk the dense code and
try to guess where it went wrong. Needing to dive into kernel code is
itself a fairly high barrier for a lot of people.
To improve this situation, let's deploy extensive pr_debug markers at
logical parse points, and add comments to the dense parsing logic. It
let's you see exactly where the parsing aborts, the string the kernel
received (useful when dealing with shell code), how it translated the
buffers to binary data, and how it will apply the mask at runtime.
p[-1] = '\0';
- if (!e->magic[0])
+ if (p == e->magic)
That code makes no sense whatsoever - if p *was* equal to e->magic, the
assignment before it would be obviously broken. And a few lines later we
have another chunk just like that.
Moreover, if you look at further context, you'll see that it's
e->magic = p;
p = scanarg(p, del);
if (!p)
sod off
p[-1] = '\0';
if (p == e->magic)
sod off
Now, that condition could be true only if scanarg(p, del) would return p,
static char *scanarg(char *s, char del)
{
char c;
while ((c = *s++) != del) {
if (c == '\\' && *s == 'x') {
s++;
if (!isxdigit(*s++))
return NULL;
if (!isxdigit(*s++))
return NULL;
}
}
return s;
}
See that s++ in the loop condition? There's no way in hell it would *not*
increment s. If we return non-NULL, we know that c was equal to del *and*
c is equal to previous_value_of_s[0], i.e. s[-1]. IOW, return value points
to the byte following the first instance of delimeter starting at the argument.
And p[-1] = '\0' replaces delimiter with NUL. IOW, the checks used to be
correct. And got buggered.
the checks are not correct. magic & mask are binary fields hence checking for a
NUL byte to indicate string parsing failed makes no sense. your patch restores
the bug i attempted to fix -- if i wanted to ignore the first byte of the file
by setting the mask or magic to 0, then binfmt_misc will wrongly reject it.

the current code does reject some bad inputs -- namely, when the magic or mask
is not specified. i was counting on the scanarg not incrementing the pointer in
that case, but as you pointed out, that doesn't work since the func always
increments the pointer. i'll see if i can handle both cases.
Post by Al Viro
Subject: unfuck fs/binfmt_misc.c
Undo some of the "prettifying" braindamage.
commit 7d65cf10e3d7747033b83fa18c5f3d2a498f66bc has merged at this point, but
the attribution to e6084d4 is wrong. of course coding style changes &
functional changes shouldn't be done which is why i didn't do it. the change
you're referring to above has nothing to do e6084d4 as it was added before that
in 6b899c4e9a049dfca759d990bd53b14f81c3626c (where i added extended debug
output). arguably those few non-debug related lines shouldn't be in that
commit, but it's a long cry from style changes.
-mike

Loading...