Skip to content

interpreters/duktape: Handle SIGINT gracefully to prevent crash on restart - #3859

Open
Sarthak-Pandey wants to merge 1 commit into
apache:masterfrom
Sarthak-Pandey:fix/duktape-sigint-crash
Open

Sarthak-Pandey wants to merge 1 commit into
apache:masterfrom
Sarthak-Pandey:fix/duktape-sigint-crash

Conversation

@Sarthak-Pandey

Copy link
Copy Markdown

Summary

Fixes #2855: Duktape crashes while you try to start it after interrupting it with ctrl+c.

When Duktape was interrupted via Ctrl+C (SIGINT), the task was abruptly terminated by NuttX's default abnormal termination handler without destroying the Duktape heap. On flat-memory architectures such as ESP32, this leaked heap allocations and corrupted internal state, causing subsequent invocations of duk to crash or panic.

This PR:

  1. Enables DUK_CMDLINE_SIGNAL in apps/interpreters/duktape/Makefile.
  2. Defines DUK_USE_INTERRUPT_COUNTER and DUK_USE_EXEC_TIMEOUT_CHECK in duk_config.h so the Duktape bytecode executor periodically checks for execution interruption.
  3. Implements an async-signal-safe interrupt handler and execution timeout check in duk_cmdline.c that safely halts bytecode execution, unwinds the stack, and invokes destroy_duktape_heap() to return all allocated memory before exiting.

Impact

  • Users can safely interrupt running Duktape scripts with Ctrl+C and immediately run them again without board crashes or heap exhaustion.

Testing

  • Verified patch applies cleanly against Duktape 2.5.0.
  • Verified signal handler registers and timeout check is hooked properly during bytecode execution.

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

this is a duktape limitation, should we send patch to duktape community instead?

@Sarthak-Pandey

Copy link
Copy Markdown
Author

Hi @xiaoxiang781216,

You are right that this is an upstream Duktape cmdline limitation. However:

  1. The upstream Duktape repository (v2.5.0) is largely inactive/dormant, so an upstream fix is unlikely to be released or backported anytime soon.
  2. NuttX already carries duk_cmdline.patch specifically to adapt duk_cmdline.c and duk_config.h for NuttX environments (e.g. LINEBUF_SIZE and date/time configs).
  3. On flat-memory/RTOS targets like ESP32, an unhandled SIGINT aborts the task and leaks Duktape's heap, making subsequent runs immediately crash the board ([BUG] Duktape crashes while you try to start it after interrupting it with ctrl+c. #2855).

Since we already maintain duk_cmdline.patch in tree, this change simply leverages Duktape's existing timeout/interrupt hooks within our patch to make duk usable and safe from board panics on NuttX.

Would you prefer keeping this in our downstream patch, or do you suggest an alternative approach (e.g., an external wrapper)?

Comment thread interpreters/duktape/duk_cmdline.patch Outdated

duk_push_global_object(ctx); /* 'this' binding */
+#if defined(DUK_CMDLINE_SIGNAL)
+ g_executing = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why need g_executing flag

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original intention was to guard the timeout callback so it only aborts when active bytecode execution (duk_call_method) is taking place, avoiding unintended aborts if a signal arrived outside the execution phase.

However, since DUK_USE_EXEC_TIMEOUT_CHECK is only ever invoked internally by Duktape's bytecode interpreter loop during active execution, checking g_interrupted alone is sufficient. g_executing is indeed redundant—I will remove it and simplify the check to return g_interrupted;.

#undef DUK_USE_EXEC_PREFER_SIZE
#define DUK_USE_EXEC_REGCONST_OPTIMIZE
-#undef DUK_USE_EXEC_TIMEOUT_CHECK
+#define DUK_USE_EXEC_TIMEOUT_CHECK(udata) duk_cmdline_timeout_check(udata)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return g_interrupted directly

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Simplified duk_cmdline_timeout_check() to directly return g_interrupted; and removed g_executing. Updated the patch and force-pushed.

@@ -44,3 +124,33 @@ diff --color -ur duktape/src-noline/duk_config.h duktape-2.5.0-modifed/src-nolin
-#define DUK_USE_DATE_PRS_STRPTIME
+#undef DUK_USE_DATE_PRS_STRPTIME

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why change

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually not a change introduced in this PR—it is pre-existing context in duk_cmdline.patch on master.

Because duk_cmdline.patch is itself a diff file, git unified diff includes those preceding - and + lines as 3 lines of unchanged context above the new DUK_USE_INTERRUPT_COUNTER hunk, which GitHub renders in red/green. I haven't modified DUK_USE_DATE_PRS_STRPTIME.


#if 0 /* XXX: safe condition? */
@@ -2962,7 +2962,17 @@ typedef struct duk_hthread duk_context;
#undef DUK_USE_EXEC_INDIRECT_BOUND_CHECK

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why add the unrelated macro

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are actually not new macros being added—they are pre-existing context lines from upstream duk_config.h right above DUK_USE_EXEC_TIMEOUT_CHECK.

The #if 0 /* XXX: safe condition? */ line above the hunk header was a stray formatting artifact from the patch file. I have cleaned up the patch so only the necessary timeout and interrupt configurations are touched.

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

Hi @xiaoxiang781216,

You are right that this is an upstream Duktape cmdline limitation. However:

  1. The upstream Duktape repository (v2.5.0) is largely inactive/dormant, so an upstream fix is unlikely to be released or backported anytime soon.
  2. NuttX already carries duk_cmdline.patch specifically to adapt duk_cmdline.c and duk_config.h for NuttX environments (e.g. LINEBUF_SIZE and date/time configs).
  3. On flat-memory/RTOS targets like ESP32, an unhandled SIGINT aborts the task and leaks Duktape's heap, making subsequent runs immediately crash the board ([BUG] Duktape crashes while you try to start it after interrupting it with ctrl+c. #2855).

Since we already maintain duk_cmdline.patch in tree, this change simply leverages Duktape's existing timeout/interrupt hooks within our patch to make duk usable and safe from board panics on NuttX.

Would you prefer keeping this in our downstream patch, or do you suggest an alternative approach (e.g., an external wrapper)?

ok, but please remove the unnecessary change from the patch file.

…start

When Duktape is interrupted via Ctrl+C (SIGINT), the task was abruptly
terminated by NuttX's default abnormal termination handler without
destroying the Duktape heap. On flat-memory architectures such as ESP32,
this leaked heap allocations and corrupted internal state, causing subsequent
invocations of 'duk' to crash the board.

This patch:
1. Enables DUK_CMDLINE_SIGNAL in Makefile to register a SIGINT handler.
2. Defines DUK_USE_INTERRUPT_COUNTER and DUK_USE_EXEC_TIMEOUT_CHECK in
   duk_config.h to periodically inspect the interruption state.
3. Implements an async-signal-safe interrupt handler and execution timeout
   check in duk_cmdline.c that stops bytecode execution cleanly, unwinds
   the call stack, and safely invokes destroy_duktape_heap() before exiting.

Fixes apache#2855

Signed-off-by: Sarthak Pandey <sarthak@email.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Duktape crashes while you try to start it after interrupting it with ctrl+c.

2 participants