From d8fdd380f39aa413886c61c2e3624ba3b4582dc0 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sun, 9 Jul 2017 04:30:36 +0200 Subject: [PATCH] tccpp: Fix corner case See added testcase 19.c from a bug report. The problem is reading outside the arguments buffers, even though we aren't allowed to. The single can_read_stream variable is not enough, sometimes we need to be able to pop into outer contexts but not into arbitrarily outside contexts. The trick is to terminate argument tokens with a EOF (and not just with 0 that makes us pop contexts), and deal with that in the few places we have to, This enables some cleanups of the can_read_stream variable use. --- tccpp.c | 14 ++++--- tests/pp/19.c | 101 +++++++++++++++++++++++++++++++++++++++++++++ tests/pp/19.expect | 14 +++++++ tests/pp/20.c | 13 ++++++ tests/pp/20.expect | 6 +++ tests/pp/Makefile | 5 +-- 6 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 tests/pp/19.c create mode 100644 tests/pp/19.expect create mode 100644 tests/pp/20.c create mode 100644 tests/pp/20.expect diff --git a/tccpp.c b/tccpp.c index d70ddd6..7eb4501 100644 --- a/tccpp.c +++ b/tccpp.c @@ -2887,6 +2887,7 @@ static void next_nomacro_spc(void) } else { next_nomacro1(); } + //printf("token = %s\n", get_tok_str(tok, &tokc)); } ST_FUNC void next_nomacro(void) @@ -2932,7 +2933,7 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) cstr_ccat(&cstr, '\"'); st = s->d; spc = 0; - while (*st) { + while (*st >= 0) { TOK_GET(&t, &st, &cval); if (t != TOK_PLCHLDR && t != TOK_NOSUBST @@ -2972,7 +2973,7 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) /* special case for var arg macros : ## eats the ',' if empty VA_ARGS variable. */ if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) { - if (*st == 0) { + if (*st <= 0) { /* suppress ',' '##' */ str.len -= 2; } else { @@ -2984,12 +2985,11 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) for(;;) { int t1; TOK_GET(&t1, &st, &cval); - if (!t1) + if (t1 <= 0) break; tok_str_add2(&str, t1, &cval); } } - } else { add_var: /* NOTE: the stream cannot be read when macro @@ -3031,7 +3031,7 @@ static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *w while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t) tok_str_add(ws_str, t), t = *++p; } - if (t == 0 && can_read_stream) { + if (t == 0) { end_macro(); /* also, end of scope for nested defined symbol */ sa = *nested_list; @@ -3197,6 +3197,7 @@ static int macro_subst_tok( if (parlevel) expect(")"); str.len -= spc; + tok_str_add(&str, -1); tok_str_add(&str, 0); sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); sa1->d = str.str; @@ -3366,7 +3367,7 @@ static void macro_subst( while (1) { TOK_GET(&t, &ptr, &cval); - if (t == 0) + if (t <= 0) break; if (t >= TOK_IDENT && 0 == nosubst) { @@ -3703,6 +3704,7 @@ static int pp_need_space(int a, int b) : '+' == a ? TOK_INC == b || '+' == b : '-' == a ? TOK_DEC == b || '-' == b : a >= TOK_IDENT ? b >= TOK_IDENT + : a == TOK_PPNUM ? b >= TOK_IDENT : 0; } diff --git a/tests/pp/19.c b/tests/pp/19.c new file mode 100644 index 0000000..aa91abe --- /dev/null +++ b/tests/pp/19.c @@ -0,0 +1,101 @@ +#define M_C2I(a, ...) a ## __VA_ARGS__ +#define M_C(a, ...) M_C2I(a, __VA_ARGS__) +#define M_C3I(a, b, ...) a ## b ## __VA_ARGS__ +#define M_C3(a, b, ...) M_C3I(a ,b, __VA_ARGS__) + +#define M_RETI_ARG2(a, b, ...) b +#define M_RET_ARG2(...) M_RETI_ARG2(__VA_ARGS__) +#define M_RETI_ARG27(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa, ...) aa +#define M_RET_ARG27(...) M_RETI_ARG27(__VA_ARGS__) + +#define M_TOBOOLI_0 1, 0, +#define M_BOOL(x) M_RET_ARG2(M_C(M_TOBOOLI_, x), 1, useless) + +#define M_IFI_0(true_macro, ...) __VA_ARGS__ +#define M_IFI_1(true_macro, ...) true_macro +#define M_IF(c) M_C(M_IFI_, M_BOOL(c)) + +#define M_FLAT(...) __VA_ARGS__ +#define M_INVI_0 1 +#define M_INVI_1 0 +#define M_INV(x) M_C(M_INVI_, x) + +#define M_ANDI_00 0 +#define M_ANDI_01 0 +#define M_ANDI_10 0 +#define M_ANDI_11 1 +#define M_AND(x,y) M_C3(M_ANDI_, x, y) + +#define M_ORI_00 0 +#define M_ORI_01 1 +#define M_ORI_10 1 +#define M_ORI_11 1 +#define M_OR(x,y) M_C3(M_ORI_, x, y) + +#define M_COMMA_P(...) M_RET_ARG27(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless) + +#define M_EMPTYI_DETECT(...) 0, 1, +#define M_EMPTYI_P_C1(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ ()) +#define M_EMPTYI_P_C2(...) M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__) +#define M_EMPTYI_P_C3(...) M_COMMA_P(__VA_ARGS__ () ) +#define M_EMPTY_P(...) M_AND(M_EMPTYI_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTYI_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTYI_P_C3(__VA_ARGS__)))) +#define M_APPLY_FUNC2B(func, arg1, arg2) \ + M_IF(M_EMPTY_P(arg2))(,func(arg1, arg2)) +#define M_MAP2B_0(func, data, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,...) \ + M_APPLY_FUNC2B(func, data, a) M_APPLY_FUNC2B(func, data, b) M_APPLY_FUNC2B(func, data, c) \ + M_APPLY_FUNC2B(func, data, d) M_APPLY_FUNC2B(func, data, e) M_APPLY_FUNC2B(func, data, f) \ + M_APPLY_FUNC2B(func, data, g) M_APPLY_FUNC2B(func, data, h) M_APPLY_FUNC2B(func, data, i) \ + M_APPLY_FUNC2B(func, data, j) M_APPLY_FUNC2B(func, data, k) M_APPLY_FUNC2B(func, data, l) \ + M_APPLY_FUNC2B(func, data, m) M_APPLY_FUNC2B(func, data, n) M_APPLY_FUNC2B(func, data, o) \ + M_APPLY_FUNC2B(func, data, p) M_APPLY_FUNC2B(func, data, q) M_APPLY_FUNC2B(func, data, r) \ + M_APPLY_FUNC2B(func, data, s) M_APPLY_FUNC2B(func, data, t) M_APPLY_FUNC2B(func, data, u) \ + M_APPLY_FUNC2B(func, data, v) M_APPLY_FUNC2B(func, data, w) M_APPLY_FUNC2B(func, data, x) \ + M_APPLY_FUNC2B(func, data, y) M_APPLY_FUNC2B(func, data, z) +#define M_MAP2B(f, ...) M_MAP2B_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ) +#define M_INIT_INIT(a) ,a, + +#define M_GET_METHOD(method, method_default, ...) \ + M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,) + +#define M_TEST_METHOD_P(method, oplist) \ + M_BOOL(M_GET_METHOD (method, 0, M_FLAT oplist)) + +#define TRUE 1 +#define TEST1(n) \ + M_IF(n)(ok,nok) +#define TEST2(op) \ + M_TEST_METHOD_P(INIT, op) +#define TEST3(op) \ + M_IF(M_TEST_METHOD_P(INIT, op))(ok, nok) +#define TEST4(op) \ + TEST1(TEST2(op)) +#define KO(a) ((void)1) + +/* This checks that the various expansions that ultimately lead to + something like 'KO(arg,arg)', where 'KO' comes from a macro + expansion reducing from a large macro chain do not are regarded + as funclike macro invocation of KO. E.g. X93 and X94 expand to 'KO', + but X95 must not consume the (a,b) arguments outside the M_IF() + invocation to reduce the 'KO' macro to an invocation. Instead + X95 should reduce via M_IF(KO)(a,b) to 'a'. + + The other lines here are variations on this scheme, with X1 to + X6 coming from the bug report at + http://lists.nongnu.org/archive/html/tinycc-devel/2017-07/msg00017.html */ +X92 M_IF(KO) +X93 M_GET_METHOD(INIT, 0, INIT(KO)) +X94 M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))) +X95 M_IF(M_GET_METHOD(INIT, 0, INIT(KO)))(a,b) +X96 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO)))) +X97 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))))(ok,nok) +X98 (M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok) +X99 M_IF(M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok) +// test begins +X1 TEST1(TRUE) // ==> expect ok, get ok +// First test with a token which is not a macro +X2 TEST2((INIT(ok))) // ==> expect 1, get 1 +X3 TEST3((INIT(ok))) // ==> expect ok, get ok +// Then test with a token which is a macro, but should not be expanded. +X4 TEST2((INIT(KO))) // ==> expect 1, get 1 +X5 TEST4(INIT(KO)) +X6 TEST3((INIT(KO))) // ==> expect ok, get "error: macro 'KO' used with too many args" diff --git a/tests/pp/19.expect b/tests/pp/19.expect new file mode 100644 index 0000000..08c0858 --- /dev/null +++ b/tests/pp/19.expect @@ -0,0 +1,14 @@ +X92 M_IFI_1 +X93 KO +X94 KO +X95 a +X96 M_IFI_1 +X97 ok +X98 (1)(ok, nok) +X99 ok +X1 ok +X2 1 +X3 ok +X4 1 +X5 nok +X6 ok diff --git a/tests/pp/20.c b/tests/pp/20.c new file mode 100644 index 0000000..7944d62 --- /dev/null +++ b/tests/pp/20.c @@ -0,0 +1,13 @@ +/* Various things I encountered while hacking the pre processor */ +#define wrap(x) x +#define pr_warning(fmt, ...) printk(KERN_WARNING fmt, ##__VA_ARGS__) +#define pr_warn(x,y) pr_warning(x,y) +#define net_ratelimited_function(function, ...) function(__VA_ARGS__) +X1 net_ratelimited_function(pr_warn, "pipapo", bla); +X2 net_ratelimited_function(wrap(pr_warn), "bla", foo); +#define two m n +#define chain4(a,b,c,d) a ## b ## c ## d +X2 chain4(two,o,p,q) +X3 chain4(o,two,p,q) +X4 chain4(o,p,two,q) +X5 chain4(o,p,q,two) diff --git a/tests/pp/20.expect b/tests/pp/20.expect new file mode 100644 index 0000000..d19405d --- /dev/null +++ b/tests/pp/20.expect @@ -0,0 +1,6 @@ +X1 printk(KERN_WARNING "pipapo",bla); +X2 printk(KERN_WARNING "bla",foo); +X2 twoopq +X3 otwopq +X4 optwoq +X5 opqtwo diff --git a/tests/pp/Makefile b/tests/pp/Makefile index ba75bbc..21c9969 100644 --- a/tests/pp/Makefile +++ b/tests/pp/Makefile @@ -31,10 +31,7 @@ FILTER = 2>&1 | sed 's,$(SRC)/,,g' # automatically generate .expect files with gcc: %.expect: # %.c - gcc -E -P $< >$*.expect 2>&1 - -%.expect: # %.S - gcc -E -P $< >$*.expect 2>&1 + gcc -E -P $*.[cS] >$*.expect 2>&1 # tell make not to delete .PRECIOUS: %.expect