82 lines
2.9 KiB
Diff
82 lines
2.9 KiB
Diff
fix already in upstream
|
|
|
|
From f17f4989fa193fa8279474c5462289a3cfe69aea Mon Sep 17 00:00:00 2001
|
|
From: Mike Frysinger <vapier@chromium.org>
|
|
Date: Fri, 8 Aug 2014 09:40:25 +0900
|
|
Subject: [PATCH] linux-user: fix readlink handling with magic exe symlink
|
|
|
|
The current code always returns the length of the path when it should
|
|
be returning the number of bytes it wrote to the output string.
|
|
|
|
Further, readlink is not supposed to append a NUL byte, but the current
|
|
snprintf logic will always do just that.
|
|
|
|
Even further, if you pass in a length of 0, you're suppoesd to get back
|
|
an error (EINVAL), but the current logic just returns 0.
|
|
|
|
Further still, if there was an error reading the symlink, we should not
|
|
go ahead and try to read the target buffer as it is garbage.
|
|
|
|
Simple test for the first two issues:
|
|
$ cat test.c
|
|
int main() {
|
|
char buf[50];
|
|
size_t len;
|
|
for (len = 0; len < 10; ++len) {
|
|
memset(buf, '!', sizeof(buf));
|
|
ssize_t ret = readlink("/proc/self/exe", buf, len);
|
|
buf[20] = '\0';
|
|
printf("readlink(/proc/self/exe, {%s}, %zu) = %zi\n", buf, len, ret);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Now compare the output of the native:
|
|
$ gcc test.c -o /tmp/x
|
|
$ /tmp/x
|
|
$ strace /tmp/x
|
|
|
|
With what qemu does:
|
|
$ armv7a-cros-linux-gnueabi-gcc test.c -o /tmp/x -static
|
|
$ qemu-arm /tmp/x
|
|
$ qemu-arm -strace /tmp/x
|
|
|
|
Signed-off-by: Mike Frysinger <vapier@chromium.org>
|
|
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
|
|
---
|
|
linux-user/syscall.c | 15 +++++++++++++--
|
|
1 file changed, 13 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
|
index fccf9f0..7c108ab 100644
|
|
--- a/linux-user/syscall.c
|
|
+++ b/linux-user/syscall.c
|
|
@@ -6636,11 +6636,22 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|
p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0);
|
|
if (!p || !p2) {
|
|
ret = -TARGET_EFAULT;
|
|
+ } else if (!arg3) {
|
|
+ /* Short circuit this for the magic exe check. */
|
|
+ ret = -TARGET_EINVAL;
|
|
} else if (is_proc_myself((const char *)p, "exe")) {
|
|
char real[PATH_MAX], *temp;
|
|
temp = realpath(exec_path, real);
|
|
- ret = temp == NULL ? get_errno(-1) : strlen(real) ;
|
|
- snprintf((char *)p2, arg3, "%s", real);
|
|
+ /* Return value is # of bytes that we wrote to the buffer. */
|
|
+ if (temp == NULL) {
|
|
+ ret = get_errno(-1);
|
|
+ } else {
|
|
+ /* Don't worry about sign mismatch as earlier mapping
|
|
+ * logic would have thrown a bad address error. */
|
|
+ ret = MIN(strlen(real), arg3);
|
|
+ /* We cannot NUL terminate the string. */
|
|
+ memcpy(p2, real, ret);
|
|
+ }
|
|
} else {
|
|
ret = get_errno(readlink(path(p), p2, arg3));
|
|
}
|
|
--
|
|
2.0.0
|
|
|