aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2022-04-26 12:22:37 +0100
committerDaniel Thompson <daniel.thompson@linaro.org>2022-04-26 12:22:37 +0100
commit22c2305444157f520cec2c5e8ced9f133ac3b99d (patch)
tree270d6b1cb3041973b29c5d30a2692091b42fe5c1
parentf443e374ae131c168a065ea1748feac6b2e76613 (diff)
downloadlinux-22c2305444157f520cec2c5e8ced9f133ac3b99d.tar.gz
arm64: mte: Handle zero-length reads in access_remote_tags()
Currently, if the user sends uiov.iov_len == 0 then __access_remote_tags() will choose its return value based on an undefined value found on the stack. I think it can only ever leak the value of the sign-bit but a leak is a leak so let's fix this by explicitly handling zero length requests. The choice of return code is not entirely clear-cut. However I think the most reasonable return value is to confirm that we have successfully performed no work. Discovered whilst experimenting with clang-analyzer (13). The problem was reported by the tooling as follows: arch/arm64/kernel/mte.c:398:11: warning: The left operand of '<=' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult] if (ret <= 0) ^ .../arch/arm64/kernel/mte.c:418:6: note: Assuming 'mm' is non-null if (!mm) ^~~ .../arch/arm64/kernel/mte.c:418:2: note: Taking false branch if (!mm) ^ .../arch/arm64/kernel/mte.c:421:6: note: Assuming field 'ptrace' is not equal to 0 if (!tsk->ptrace || (current != tsk->parent) || ^~~~~~~~~~~~ .../arch/arm64/kernel/mte.c:421:6: note: Left side of '||' is false .../arch/arm64/kernel/mte.c:421:23: note: Assuming the condition is false if (!tsk->ptrace || (current != tsk->parent) || ^ ./arch/arm64/include/asm/current.h:24:17: note: expanded from macro 'current' ^ .../arch/arm64/kernel/mte.c:421:6: note: Left side of '||' is false if (!tsk->ptrace || (current != tsk->parent) || ^ .../arch/arm64/kernel/mte.c:422:8: note: Assuming the condition is false ((get_dumpable(mm) != SUID_DUMP_USER) && ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../arch/arm64/kernel/mte.c:422:44: note: Left side of '&&' is false ((get_dumpable(mm) != SUID_DUMP_USER) && ^ .../arch/arm64/kernel/mte.c:428:8: note: Calling '__access_remote_tags' ret = __access_remote_tags(mm, addr, kiov, gup_flags); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../arch/arm64/kernel/mte.c:338:2: note: 'ret' declared without an initial value int ret; ^~~~~~~ .../arch/arm64/kernel/mte.c:341:6: note: Assuming the condition is false if (!access_ok(buf, len)) ^~~~~~~~~~~~~~~~~~~~ .../arch/arm64/kernel/mte.c:341:2: note: Taking false branch if (!access_ok(buf, len)) ^ .../arch/arm64/kernel/mte.c:344:6: note: Calling 'mmap_read_lock_killable' if (mmap_read_lock_killable(mm)) ^~~~~~~~~~~~~~~~~~~~~~~~~~~ ..././include/linux/mmap_lock.h:127:48: note: Assuming 'ret' is equal to 0, which participates in a condition later __mmap_lock_trace_acquire_returned(mm, false, ret == 0); ^~~~~~~~ ..././include/linux/mmap_lock.h:128:2: note: Returning zero (loaded from 'ret'), which participates in a condition later return ret; ^~~~~~~~~~ .../arch/arm64/kernel/mte.c:344:6: note: Returning from 'mmap_read_lock_killable' if (mmap_read_lock_killable(mm)) ^~~~~~~~~~~~~~~~~~~~~~~~~~~ .../arch/arm64/kernel/mte.c:344:2: note: Taking false branch if (mmap_read_lock_killable(mm)) ^ .../arch/arm64/kernel/mte.c:347:2: note: Loop condition is false. Execution continues on line 392 while (len) { ^ .../arch/arm64/kernel/mte.c:396:13: note: Field 'iov_len' is 0 if (!kiov->iov_len) { ^ .../arch/arm64/kernel/mte.c:396:2: note: Taking true branch if (!kiov->iov_len) { ^ .../arch/arm64/kernel/mte.c:398:11: note: The left operand of '<=' is a garbage value if (ret <= 0) ~~~ ^ Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
-rw-r--r--arch/arm64/kernel/mte.c3
1 files changed, 3 insertions, 0 deletions
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index f418ebc65f95..a6905284b0f4 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -338,6 +338,9 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr,
int ret;
int write = gup_flags & FOLL_WRITE;
+ if (len == 0)
+ return 0;
+
if (!access_ok(buf, len))
return -EFAULT;