From cd610905162812ca7dcf246b3dc46bce5e28e7cb Mon Sep 17 00:00:00 2001 Message-ID: From: Kerin Millar Date: Sun, 31 May 2026 13:36:09 +0800 Subject: [PATCH 1/2] builtin: Fix octal escapes in dollar-single-quotes The test suite of gentoo-functions recently uncovered a bug concerning the handling of octal escape sequences within dollar-single-quotes. Here is a reproducer: $ dash -c "x=\$'\\201'; printf '%s' \"\$x\"" | od -An -tx1 88 That is, despite the input being 0x81, the output is 0x88. Indeed, any input between 0x81..0x88 is adversely affected and some - such as 0x82 - induce a segfault. I noticed the following macros in src/parser.h: #define CTL_FIRST -127 /* first 'special' character */ #define CTLESC -127 /* escape next character */ #define CTL_LAST -120 /* last 'special' character */ Reinterpreted as unsigned char, the CTL_FIRST..CTL_LAST range maps exactly to 0x81..0x88: $ perl -e 'printf "%x\0", $_ & 0xFF for -127..-120' | xargs -0 81 82 83 84 85 86 87 88 From this, I was able to deduce that all of these bytes must be preceded by CTLESC in order to be taken literally. Make it so. Link: https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=947090fb7704 Signed-off-by: Kerin Millar The escape should also be applied to \x sequences. In fact \x81 was totally broken as it produced a multi-byte character. Fix this by merging these two code paths. Signed-off-by: Herbert Xu --- src/bltin/printf.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bltin/printf.c b/src/bltin/printf.c index 106aecd..671e781 100644 --- a/src/bltin/printf.c +++ b/src/bltin/printf.c @@ -332,6 +332,7 @@ unsigned conv_escape(char *str0, char *out0, bool mbchar) char *out = out0; char *str = str0; unsigned value; + int och; int ch; ch = *str; @@ -359,12 +360,18 @@ unsigned conv_escape(char *str0, char *out0, bool mbchar) } str--; + +check_value: + if (mbchar && (signed char)value >= CTL_FIRST && + (signed char)value <= CTL_LAST) + USTPUTC(CTLESC, out); break; case 'x': ch = 2; hex: + och = ch; value = 0; do { int c = *++str; @@ -391,6 +398,9 @@ hex: if (value < 0x80) break; + if (och <= 2) + goto check_value; + if (value < 0x110000) { int mboff = (mbchar - 1) * 2; unsigned uni = value; -- 2.54.0