Skip to content

Conversation

@Gary-Hobson
Copy link
Contributor

Summary

This PR fixes a critical bug where pressing Ctrl-C in a serial console does not deliver SIGINT to the foreground process, preventing proper process termination.

Root Cause:

The serial driver called nxsig_tgkill(-1, dev->pid, signo) from interrupt context. With pid=-1, nxsig_dispatch() was invoked with thread=true, which requires stcb->group == this_task()->group. However, in interrupt context, this_task() returns the IDLE task, whose group differs from the target process group. This caused the signal dispatch to fail with -ESRCH.

Changes:

  • Replace nxsig_tgkill(-1, pid, signo) with nxsig_kill(pid, signo) in serial_dma.c
  • Replace nxsig_tgkill(-1, pid, signo) with nxsig_kill(pid, signo) in serial_io.c

The nxsig_kill() function uses thread=false, which routes through group_signal() without the same-group check, allowing signals to be delivered correctly from interrupt context.

Impact

  • Features: Fixes Ctrl-C signal delivery in serial console environments
  • User Experience: Users can now properly terminate foreground processes with Ctrl-C
  • Build: No build system changes required
  • Hardware: Affects all platforms using serial console with signal support
  • Documentation: No documentation changes needed
  • Security: No security implications
  • Compatibility: Fully backward compatible, no API changes

Testing

Tested on sim and arm64 platforms with serial console configurations.

…l-C.

When pressing Ctrl-C, the foreground process did not receive SIGINT
and failed to terminate.

The serial driver called nxsig_tgkill(-1, dev->pid, signo) from
interrupt context. With pid=-1, nxsig_dispatch() was called with
thread=true, which requires stcb->group == this_task()->group.
However, in interrupt context, this_task() returns the IDLE task,
whose group differs from the target process group. This caused
the signal dispatch to fail with -ESRCH.

Solution:
Replace nxsig_tgkill(-1, pid, signo) with nxsig_kill(pid, signo).
nxsig_kill() uses thread=false, which routes through group_signal()
without the same-group check, allowing signals to be delivered
correctly from interrupt context.

Impact:
- Fixes Ctrl-C signal delivery in serial console
- No API changes
- Affects serial driver interrupt handling only

Testing: Verified on QEMU ARM64 simulator with serial console

Signed-off-by: yinshengkai <yinshengkai@bytedance.com>
@github-actions github-actions bot added Area: Drivers Drivers issues Size: XS The size of the change in this PR is very small labels Jan 22, 2026
@acassis
Copy link
Contributor

acassis commented Jan 22, 2026

@Gary-Hobson I didn't know that Ctrl+C should hit a process running in background, but I double checked it on Linux and in fact it worked, but there are a catch:

If the process is running in foregroup Ctrl+C will terminate it:

alan@dev:~/nuttxspace/nuttx$ top
Ctrl+C
$ ps aux | grep top
// There is not top running.

When the process is running in backgroup Ctrl+C just stops it, but it still there, see:

alan@dev:~/nuttxspace/nuttx$ top &
[1] 96099
alan@dev:~/nuttxspace/nuttx$ ^C

[1]+  Stopped                 top
alan@dev:~/nuttxspace/nuttx$ ps
    PID TTY          TIME CMD
   9921 pts/0    00:00:00 bash
  96099 pts/0    00:00:00 top
  96104 pts/0    00:00:00 ps
alan@dev:~/nuttxspace/nuttx$ ps aux | grep top
alan       96099  0.0  0.0  12708  4008 pts/0    T    12:20   0:00 top
alan       96108  0.0  0.0   9288  2372 pts/0    S+   12:21   0:00 grep --color=auto top

Are you replicating this behavior, or Ctrl+C will also terminate the process running in background?

I think we should replicate the Linux behavior :-)

@acassis
Copy link
Contributor

acassis commented Jan 22, 2026

@cederom do you know if FreeBSD also do like Linux in this case?

@acassis acassis requested review from cederom and linguini1 January 22, 2026 15:30
@cederom
Copy link
Contributor

cederom commented Jan 22, 2026

@cederom do you know if FreeBSD also do like Linux in this case?

This looks bad idea to use Ctrl+C to kill background jobs, because you can kill many jobs that way accidentally? Ctrl+C generates a signal that is handled by current application only.

I can replicate this behavior but this seems bash specific, zsh does not behave this way. Also there is a difference if task is moved to background by Ctrl+Z or launched with & (i.e. no stop signal in bash after Ctrl+Z).

You need to fg and then Ctrl+C sends the signal to the current process in the foreground. This seems most intuitive and simple solution? :-)

Tested in zsh and bash (note I have two top already running as tmux plugins):

/// ZSH

hexagon% ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
10997  4  SN+  0:00,00 grep top
hexagon% top
<-- Ctrl+Z
zsh: suspended  top
hexagon% <-- Ctrl+C does nothing.
hexagon% ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
11197  4  TN   0:00,01 top
11606  4  SN+  0:00,00 grep top
hexagon% fg
[3]    continued  top <-- Ctrl+C kills top.
hexagon% ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top

/// BASH

[cederom@hexagon ~]$ ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
25260  4  RN+  0:00,00 grep top
[cederom@hexagon ~]$ top <-- Ctrl+C exits top.
[cederom@hexagon ~]$ ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
25529  4  SN+  0:00,00 grep top
[cederom@hexagon ~]$ top <-- Ctrl+Z brings top to back.

[1]+  Stopped                    top
[cederom@hexagon ~]$ ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
25716  4  TN   0:00,00 top <-- here it is.
[cederom@hexagon ~]$ ^C <-- Ctrl+C does nothing here.
[cederom@hexagon ~]$ ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
25716  4  TN   0:00,00 top <-- top still here.
[cederom@hexagon ~]$ fg <-- bring top to foreground.
top <-- Ctrl+X kills top.
[cederom@hexagon ~]$ ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
26616  4  SN+  0:00,00 grep top

Now top &:

/// BASH

[cederom@hexagon ~]$ top &
[1] 39248
[cederom@hexagon ~]$ ^C

[1]+  Stopped                    top
[cederom@hexagon ~]$ ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
39248  4  TN   0:00,00 top
39530  4  SN+  0:00,00 grep top
[cederom@hexagon ~]$ fg
top

/// ZSH

hexagon% ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
42465  4  SN+  0:00,00 grep top
hexagon% top &
[3] 42632
hexagon%
[3]  + suspended (tty output)  top
hexagon%
hexagon% ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
42632  4  TN   0:00,00 top
43518  4  SN+  0:00,01 grep top
hexagon% fg
[3]    continued  top
hexagon% ps | grep top
 4685  4  TN   0:00,00 top
 5885  4  TN   0:00,00 top
43926  4  SN+  0:00,00 grep top

@cederom
Copy link
Contributor

cederom commented Jan 22, 2026

I will test and verify fix on a real hardware in a free moment just to confirm, thanks @Gary-Hobson and @acassis :-)

@acassis
Copy link
Contributor

acassis commented Jan 22, 2026

@cederom I think Ctrl+C will break only the last job sent to background:

$ gedit &
[1] 104015

$ top &
[2] 104029
$ ^C

[2]+  Stopped                 top
$

In fact this is a good improvement, we just need to guarantee that it is doing the right thing.

@Gary-Hobson please test sending two process to background and test to confirm it will only terminate the last one.

@cederom
Copy link
Contributor

cederom commented Jan 22, 2026

Yeah, but its bash only, and Ctrl+C does not kill the background process, you can still bring it back working with fg :-)

@Gary-Hobson
Copy link
Contributor Author

@cederom I think Ctrl+C will break only the last job sent to background:

$ gedit &
[1] 104015

$ top &
[2] 104029
$ ^C

[2]+  Stopped                 top
$

In fact this is a good improvement, we just need to guarantee that it is doing the right thing.

@Gary-Hobson please test sending two process to background and test to confirm it will only terminate the last one.

The current code doesn't terminate any background processes, and as I see it, terminating background processes probably isn't a good idea.

Below are the steps for my test:

  1. Modify the code in hello_main.c:
int main(int argc, FAR char *argv[])
{
  printf("Hello, World!!\n");
  sleep(30);
  return 0;
}
  1. Configure and compile the project:
./tools/configure.sh mps2-an500/nsh
make menuconfig

# Add the following two configurations:
CONFIG_SIG_DEFAULT=y
CONFIG_TTY_SIGINT=y

make -j

Run the test on QEMU:

qemu-system-arm -M mps2-an500 -cpu cortex-m7 -nographic -kernel ./nuttx

nsh> hello
press Ctrl+C
nxtask_exit: hello pid=3,TCB=0x60007410
nsh>

@acassis
Copy link
Contributor

acassis commented Jan 23, 2026

@cederom I think Ctrl+C will break only the last job sent to background:

$ gedit &
[1] 104015

$ top &
[2] 104029
$ ^C

[2]+  Stopped                 top
$

In fact this is a good improvement, we just need to guarantee that it is doing the right thing.
@Gary-Hobson please test sending two process to background and test to confirm it will only terminate the last one.

The current code doesn't terminate any background processes, and as I see it, terminating background processes probably isn't a good idea.

Below are the steps for my test:

1. Modify the code in hello_main.c:
int main(int argc, FAR char *argv[])
{
  printf("Hello, World!!\n");
  sleep(30);
  return 0;
}
2. Configure and compile the project:
./tools/configure.sh mps2-an500/nsh
make menuconfig

# Add the following two configurations:
CONFIG_SIG_DEFAULT=y
CONFIG_TTY_SIGINT=y

make -j

Run the test on QEMU:

qemu-system-arm -M mps2-an500 -cpu cortex-m7 -nographic -kernel ./nuttx

nsh> hello
press Ctrl+C
nxtask_exit: hello pid=3,TCB=0x60007410
nsh>

@Gary-Hobson yes, I think I read background instead foreground and decided to test it on Linux. But I agree it not a good idea to kill a background process using Ctrl+C. I was surprised to see the Linux shell doing it.

@xiaoxiang781216 xiaoxiang781216 merged commit 2bfff20 into apache:master Jan 24, 2026
49 of 77 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Drivers Drivers issues Size: XS The size of the change in this PR is very small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants