arch/arm: carry CONTROL over to the fork child on Cortex-M - #19565
Merged
xiaoxiang781216 merged 1 commit intoJul 28, 2026
Conversation
In a protected build arm_svcall.c treats the caller's CONTROL as part of the saved system call state: it stores it in xcp.syscall[].ctrlreturn on entry and restores it from there on SYS_syscall_return. All three Cortex-M profiles do this -- armv6-m, armv7-m and armv8-m each define the field in arch/arm/include/<arch>/irq.h and use it symmetrically. arm_fork_direct() copied sysreturn and excreturn to the child but not ctrlreturn. The child's TCB comes from kmm_zalloc(), so the field was zero, and CONTROL == 0 is nPRIV clear: the child returned to user space privileged while its parent returned unprivileged. The child ran out its life with the MPU restrictions its parent is under silently lifted, which is the isolation BUILD_PROTECTED exists to provide. Nothing faults, and that is why this survived. CONTROL == 0 also selects MSP, which sounds like it should crash immediately, but NuttX already runs Cortex-M threads on MSP -- the parent's saved value is 0x1, nPRIV set and SPSEL clear -- so the two differ only in the privilege bit and there is no stack change to trip over. Privileged code then passes every test unprivileged code passes, so ostest cannot see it either. Measured on an RP2350 (Cortex-M33) in BUILD_PROTECTED, breaking at the nxtask_start_fork() call in arm_fork_direct() during task_fork_test: parent ctrlreturn 0x00000001, child ctrlreturn 0x00000000. With this change both read 0x00000001. BUILD_FLAT is unaffected: without CONFIG_LIB_SYSCALL, nsyscalls is 0 and the whole block is skipped. armv7-a and armv7-r are unaffected too; they carry cpsr instead, and that is already copied. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
casaroli
force-pushed
the
fix-cortexm-fork-ctrlreturn
branch
from
July 28, 2026 07:11
3a46a68 to
8538145
Compare
xiaoxiang781216
approved these changes
Jul 28, 2026
casaroli
marked this pull request as ready for review
July 28, 2026 07:18
jerpelea
approved these changes
Jul 28, 2026
jerpelea
requested changes
Jul 28, 2026
jerpelea
left a comment
Contributor
There was a problem hiding this comment.
please replace
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
with
Assisted-by: Claude Opus 5 (1M context) noreply@anthropic.com
Contributor
Author
should we add a check in the CI to block it automatically and not let the CI run? |
casaroli
force-pushed
the
fix-cortexm-fork-ctrlreturn
branch
from
July 28, 2026 07:53
8538145 to
5d13ebd
Compare
Contributor
|
@casaroli please do |
casaroli
force-pushed
the
fix-cortexm-fork-ctrlreturn
branch
from
July 28, 2026 07:55
5d13ebd to
1149269
Compare
jerpelea
approved these changes
Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note: Please adhere to Contributing Guidelines.
Summary
arm_fork()copies the caller's saved system call state to the child so thatthe child can return through
SYS_syscall_returnthe way its parent would.It copies
sysreturnandexcreturn, but notctrlreturn.On Cortex-M in a protected build,
arm_svcall.ctreats CONTROL as part of thatsaved state: it stores the caller's CONTROL in
xcp.syscall[].ctrlreturnonentry and restores it from there on
SYS_syscall_return. All three Cortex-Mprofiles do this — armv6-m, armv7-m and armv8-m each define the field in
arch/arm/include/<arch>/irq.hand use it symmetrically.The child's TCB comes from
kmm_zalloc(), so the field is zero, andCONTROL == 0is nPRIV clear — privileged. The child therefore returns touser space privileged while its parent returns unprivileged, and runs out its
life with the MPU restrictions its parent is under silently lifted. That is
precisely the isolation
CONFIG_BUILD_PROTECTEDexists to provide.Impact
A
fork()child inCONFIG_BUILD_PROTECTEDon Cortex-M currently executeswith kernel privilege. This restores it to the parent's.
Nothing faults without the fix, which is why it has survived:
CONTROL == 0also selects MSP, which sounds like it should crash immediately, but NuttX
already runs Cortex-M threads on MSP — the parent's saved value is
0x1, nPRIVset and SPSEL clear — so the two differ only in the privilege bit and there is
no stack change to trip over. Privileged code then passes every test
unprivileged code passes, so
ostestcannot see it either.CONFIG_BUILD_FLATis unaffected: withoutCONFIG_LIB_SYSCALL,nsyscallsis0 and the whole block is skipped. armv7-a and armv7-r are unaffected too; they
carry
cpsrinstead, and that is already copied.Testing
Measured on a Raspberry Pi RP2350 (Cortex-M33) in
BUILD_PROTECTED, breakingat the
nxtask_start_fork()call during a fork test:With this change both read
0x00000001.Build-tested
mps2-an521:nsh(armv8-m) on macOS/arm64 with Arm GNUarm-none-eabi-gcc14.2.Rel1.tools/checkpatch.sh -c -u -m -gclean.