1
#!/bin/sh --

for set_trap_sig in HUP INT QUIT ALRM TERM; do
    trap -- '
        trap -- - '"${set_trap_sig:?}"' EXIT || exit "$?"
        kill -s '"${set_trap_sig:?}"' -- "$$" || exit "$?"' "$set_trap_sig"
done

sleep 15 || exit "$?"

Here's what happens when I send SIGINT to the script

user@hostname:/tmp$ ./script.sh
^C./script.sh: 3: ./script.sh: Syntax error: EOF in backquote substitution

This issue seems specific to dash. On ash, bash and ksh93 I do not get this error. This is particularly weird because my script does not even contain the backquote character.

If I remove the double quotes on the trailing $? on line 5 the error goes away.

Am I doing something stupid or is dash misbehaving? Please, no comments about the level of error-checking in my scripts.

We have now established that this a very serious bug that affects even modern versions of Ubuntu and Debian. Does anyone know of a workaround?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255

1 Answers1

5

Here is a simplified testcase:

trap '
        trap    - HUP EXIT || exit "$?"
        kill -s HUP    "$$" || exit "$?" ' HUP INT
kill -HUP $$
$ dash dash-bug
dash-bug: 3: /home2/ahq/dash-bug: Syntax error: EOF in backquote substitution

Modifying the length or content of the quoted code may either "fix" the bug or produce different and interesting corruptions, with random bytes appearing out of nowhere.

This was caused by a use-after-free bug which was fixed since dash 0.5.9, but which is still present in the dash 0.5.8 from Debian 9.8 stable (stretch), Ubuntu 18.04 (bionic) and Ubuntu 18.10 (cosmic).

This is the commit which fixed it:

commit 6c3f73bc536082fec38bd36e6c8a121033c68835
Author: Herbert Xu <herbert@gondor.apana.org.au>
Date:   Thu Oct 2 08:26:06 2014 +0800

    [EVAL] Fix use-after-free in dotrap/evalstring

    The function dotrap calls evalstring using the stored trap string.
    If evalstring then unsets that exact trap string then we will end
    up using freed memory.

    This patch fixes it by making evalstring always duplicate the string
    before using it.

    Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

A work-around is to put the trap handler in a function -- so that no commands run after the trap has been unset try to use the action string after it has been freed:

sighandler(){
    trap - "$sig" EXIT || exit "$?"
    kill -s "$sig" "$$" || exit "$?"
}
for sig in HUP INT QUIT ALRM TERM; do
    trap "sig=$sig; sighandler" "$sig"
done

kill -s HUP "$$"
  • Does the error go away if you fix the nested single quotes? Something like trap " trap -- - \"'$s'\" EXIT || exit \"$?\" kill -s HUP -- \"$$\" || exit \"$?\"" "$s" or similar (sorry, I can't quite work out the right way). – terdon Apr 20 '19 at 17:47
  • 1
    I'm just wondering if the issue is related to the nested single quotes is all. – terdon Apr 20 '19 at 18:00
  • @mosvy Is there a workaround? This is all very unsettling to me. This bug is present in Ubuntu 14.04 and 16.04. In 18.04 I do not get the error but I see no evidence that the commit you reference has actually been applied yet when I look at the changelog. – Harold Fischer Apr 21 '19 at 03:29
  • 1
    I'm not sure if there's any workaround; I'll have to check if putting the trap handler in a function completely bypasses this bug, but I cannot do that now (I'm on a phone). The dash from ubuntu 18.04 and 18.10 is the same 0.5.8 version so it's not fixed. Only the version from ubuntu 19.04 (disco) should be fixed. As all use-after-free bugs in scripting languages, this may be exploitable, though the impact should be pretty low. –  Apr 21 '19 at 12:16
  • @mosvy You mention 'random bytes appearing out of nowhere'. Does that mean there are cases where the bug surfaces and you wouldn't even notice? – Harold Fischer Apr 22 '19 at 06:37
  • Yes, try running a testcase which doesn't show the bug (eg. dash -c 'trapcmd=$(printf "trap - HUP;\nkill -s HUP \$\$"); trap "$trapcmd" HUP; kill -s HUP $$') in valgrind. I've added a workaround for your case to the answer. –  Apr 24 '19 at 01:41