> On Jan 22, 2024, at 2:40 AM, Richard Biener <richard.guent...@gmail.com> > wrote: > > On Fri, Jan 19, 2024 at 5:26 PM Qing Zhao <qing.z...@oracle.com> wrote: >> >> >> >>> On Jan 19, 2024, at 4:30 AM, Richard Biener <richard.guent...@gmail.com> >>> wrote: >>> >>> On Thu, Jan 18, 2024 at 3:46 PM Qing Zhao <qing.z...@oracle.com> wrote: >>>> >>>> >>>> >>>>> On Jan 17, 2024, at 1:43 AM, Richard Biener <richard.guent...@gmail.com> >>>>> wrote: >>>>> >>>>> On Wed, Jan 17, 2024 at 7:42 AM Richard Biener >>>>> <richard.guent...@gmail.com> wrote: >>>>>> >>>>>> On Tue, Jan 16, 2024 at 9:26 PM Qing Zhao <qing.z...@oracle.com> wrote: >>>>>>> >>>>>>> >>>>>>> >>>>>>>> On Jan 15, 2024, at 4:31 AM, Richard Biener >>>>>>>> <richard.guent...@gmail.com> wrote: >>>>>>>> >>>>>>>>> All my questions for unshare_expr relate to a LTO bug that I >>>>>>>>> currently stuck with >>>>>>>>> when using .ACCESS_WITH_SIZE in bound sanitizer (only with -flto, >>>>>>>>> without -flto, no issue): >>>>>>>>> >>>>>>>>> [opc@qinzhao-aarch64-ol8 gcc]$ sh t >>>>>>>>> during IPA pass: modref >>>>>>>>> t.c:20:1: internal compiler error: tree code ‘ssa_name’ is not >>>>>>>>> supported in LTO streams >>>>>>>>> 0x14c3993 lto_write_tree >>>>>>>>> ../../latest-gcc-write/gcc/lto-streamer-out.cc:561 >>>>>>>>> 0x14c3aeb lto_output_tree_1 >>>>>>>>> >>>>>>>>> And the value of the tree node that triggered the ICE is: >>>>>>>>> (gdb) call debug_tree(expr) >>>>>>>>> <ssa_name 0xfffff5761e60 type <error_mark 0xfffff56c0e58> >>>>>>>>> nothrow >>>>>>>>> def_stmt >>>>>>>>> version:13 in-free-list> >>>>>>>>> >>>>>>>>> Is there any good way to debug LTO bug? >>>>>>>> >>>>>>>> This happens usually when you have a VLA type and its type fields are >>>>>>>> not >>>>>>>> properly gimplified which usually happens because the frontend fails to >>>>>>>> insert a gimplification point for it (a DECL_EXPR). >>>>>>> >>>>>>> I found an old gcc bug >>>>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97172 >>>>>>> ICE: tree code ‘ssa_name’ is not supported in LTO streams since >>>>>>> r11-3303-g6450f07388f9fe57 >>>>>>> >>>>>>> Which is very similar to the bug I am having right now. >>>>>>> >>>>>>> After further study, I suspect that the issue I am having right now >>>>>>> with the LTO streaming also >>>>>>> relate to “unshare_expr”, “save_expr”, and the combination of these >>>>>>> two, I suspect that >>>>>>> the current gcc cannot handle the combination of these two correctly >>>>>>> for my case. >>>>>>> >>>>>>> My testing case is: >>>>>>> >>>>>>> #include <stdlib.h> >>>>>>> void __attribute__((__noinline__)) setup_and_test_vla (int n1, int n2, >>>>>>> int m) >>>>>>> { >>>>>>> struct foo { >>>>>>> int n; >>>>>>> int p[][n2][n1] __attribute__((counted_by(n))); >>>>>>> } *f; >>>>>>> >>>>>>> f = (struct foo *) malloc (sizeof(struct foo) + m*sizeof(int[n2][n1])); >>>>>>> f->n = m; >>>>>>> f->p[m][n2][n1]=1; >>>>>>> return; >>>>>>> } >>>>>>> >>>>>>> int main(int argc, char *argv[]) >>>>>>> { >>>>>>> setup_and_test_vla (10, 11, 20); >>>>>>> return 0; >>>>>>> } >>>>>>> >>>>>>> Failed with >>>>>>> my_gcc -Os -fsanitize=bounds -flto >>>>>>> >>>>>>> If changing either n1 or n2 to a constant, the testing passed. >>>>>>> If deleting -flto, the testing passed too. >>>>>>> >>>>>>> I double checked my code per the suggestions provided by you and Jakub >>>>>>> in this >>>>>>> email thread, and I think the code should be fine. >>>>>>> >>>>>>> The code is following: >>>>>>> >>>>>>> ===== >>>>>>> 504 /* Instrument array bounds for INDIRECT_REFs whose pointers are >>>>>>> 505 POINTER_PLUS_EXPRs of calls to .ACCESS_WITH_SIZE. We create >>>>>>> special >>>>>>> 506 builtins that gets expanded in the sanopt pass, and make an array >>>>>>> 507 dimension of it. ARRAY is the pointer to the base of the array, >>>>>>> 508 which is a call to .ACCESS_WITH_SIZE, *OFFSET is the offset to >>>>>>> the >>>>>>> 509 beginning of array. >>>>>>> 510 Return NULL_TREE if no instrumentation is emitted. */ >>>>>>> 511 >>>>>>> 512 tree >>>>>>> 513 ubsan_instrument_bounds_indirect_ref (location_t loc, tree array, >>>>>>> tree *offset) >>>>>>> 514 { >>>>>>> 515 if (!is_access_with_size_p (array)) >>>>>>> 516 return NULL_TREE; >>>>>>> 517 tree bound = get_bound_from_access_with_size (array); >>>>>>> 518 /* The type of the call to .ACCESS_WITH_SIZE is a pointer type to >>>>>>> 519 the element of the array. */ >>>>>>> 520 tree element_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE >>>>>>> (array))); >>>>>>> 521 gcc_assert (bound); >>>>>>> 522 >>>>>>> 523 /* Given the offset, and the size of each element, the index can >>>>>>> be >>>>>>> 524 computed as: offset/element_size. */ >>>>>>> 525 *offset = save_expr (*offset); >>>>>>> 526 tree index = fold_build2 (EXACT_DIV_EXPR, >>>>>>> 527 sizetype, *offset, >>>>>>> 528 unshare_expr (element_size)); >>>>>>> 529 /* Create a "(T *) 0" tree node to describe the original array >>>>>>> type. >>>>>>> 530 We get the original array type from the first argument of the >>>>>>> call to >>>>>>> 531 .ACCESS_WITH_SIZE (REF, COUNTED_BY_REF, 1, num_bytes, -1). >>>>>>> 532 >>>>>>> 533 Originally, REF is a COMPONENT_REF with the original array >>>>>>> type, >>>>>>> 534 it was converted to a pointer to an ADDR_EXPR, and the >>>>>>> ADDR_EXPR's >>>>>>> 535 first operand is the original COMPONENT_REF. */ >>>>>>> 536 tree ref = CALL_EXPR_ARG (array, 0); >>>>>>> 537 tree array_type >>>>>>> 538 = unshare_expr (TREE_TYPE (TREE_OPERAND (TREE_OPERAND(ref, 0), >>>>>>> 0))); >>>>>>> 539 tree zero_with_type = build_int_cst (build_pointer_type >>>>>>> (array_type), 0); >>>>>>> 540 return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS, >>>>>>> 541 void_type_node, 3, >>>>>>> zero_with_type, >>>>>>> 542 index, bound); >>>>>>> 543 } >>>>>>> >>>>>>> ===== >>>>>>> >>>>>>> Inside gdb, the guilty IR failed in LTO streaming is from the above >>>>>>> line 520: >>>>>>> TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array))), >>>>>>> >>>>>>> When I use this tree node as an operand of the expression at line 526, >>>>>>> I added >>>>>>> unshare_expr. >>>>>>> >>>>>>> However, I still see the guilty IR as in gdb: >>>>>>> >>>>>>> unit-size <mult_expr 0xfffff5aabf90 type <integer_type >>>>>>> 0xfffff57c0000 sizetype> >>>>>>> side-effects >>>>>>> arg:0 <mult_expr 0xfffff5aabf68 type <integer_type >>>>>>> 0xfffff57c0000 sizetype> >>>>>>> >>>>>>> arg:0 <ssa_name 0xfffff5761e18 type <error_mark >>>>>>> 0xfffff56c0e58> >>>>>>> nothrow >>>>>>> def_stmt >>>>>>> version:12 in-free-list> >>>>>>> arg:1 <ssa_name 0xfffff5761e60 type <error_mark >>>>>>> 0xfffff56c0e58> >>>>>>> nothrow >>>>>>> def_stmt >>>>>>> version:13 in-free-list>> >>>>>>> arg:1 <integer_cst 0xfffff56c10c8 constant 4>> >>>>>>> >>>>>>> >>>>>>> I have been stuck with this bug for quite some time. >>>>>>> Any help is helpful. >>>>>> >>>>>> The above hasn't been gimplified correctly, you'd instead see >>>>>> a D.1234 in there, not an expression with SSA names. That happens >>>>>> when the frontend fails to emit a DECL_EXPR for a decl with this >>>>>> type. >>>>> >>>>> .. which then also results in missing unsharing of this expression >>>>> (so the SSA names leak in) >>>> >>>> Thanks a lot for the hints. >>>> >>>> One correction first, the LTO bug is not related to -fsanitize=bounds. >>>> Deleting -fsanitize=bounds still can >>>> repeat the failure. >>>> >>>> After further debugging into the gimplification phase related with the >>>> SAVE_EXPR, I finally locate the place >>>> where the unshareing of the expression is missing. This is in the >>>> routine “pointer_int_sum” of c-family/c-common.cc: >>>> >>>> 3330 { >>>> 3331 if (!complain && !COMPLETE_TYPE_P (TREE_TYPE (result_type))) >>>> 3332 return error_mark_node; >>>> 3333 size_exp = size_in_bytes_loc (loc, TREE_TYPE (result_type)); >>>> 3334 /* Wrap the pointer expression in a SAVE_EXPR to make sure it >>>> 3335 is evaluated first when the size expression may depend >>>> 3336 on it for VM types. */ >>>> 3337 if (TREE_SIDE_EFFECTS (size_exp) >>>> 3338 && TREE_SIDE_EFFECTS (ptrop) >>>> 3339 && variably_modified_type_p (TREE_TYPE (ptrop), NULL)) >>>> 3340 { >>>> 3341 ptrop = save_expr (ptrop); >>>> 3342 size_exp = build2 (COMPOUND_EXPR, TREE_TYPE (intop), ptrop, >>>> size_exp); >>>> 3343 } >>>> 3344 } >>>> >>>> In the above, at line 3333, the tree node, TYPE_SIZE_UNIT >>>> (TREE_TYPE(result_type)), is returned directly as >>>> the size_exp, >>>> >>>> (gdb) call debug_tree(size_exp) >>>> <mult_expr 0xfffff5a6f910 >>>> type <integer_type 0xfffff57c0000 sizetype public unsigned DI >>>> size <integer_cst 0xfffff56c0e70 constant 64> >>>> unit-size <integer_cst 0xfffff56c0e88 constant 8> >>>> align:64 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type >>>> 0xfffff57c0000 precision:64 min <integer_cst 0xfffff56c0ea0 0> max >>>> <integer_cst 0xfffff56d05e0 18446744073709551615>> >>>> side-effects >>>> arg:0 <mult_expr 0xfffff5a6f8e8 type <integer_type 0xfffff57c0000 >>>> sizetype> >>>> side-effects >>>> arg:0 <nop_expr 0xfffff56dc540 type <integer_type 0xfffff57c0000 >>>> sizetype> >>>> side-effects >>>> arg:0 <save_expr 0xfffff56dc4c0 type <integer_type >>>> 0xfffff57c05e8 int> >>>> side-effects arg:0 <parm_decl 0xfffff76b6f80 n1>>> >>>> arg:1 <nop_expr 0xfffff56dc600 type <integer_type 0xfffff57c0000 >>>> sizetype> >>>> side-effects >>>> arg:0 <save_expr 0xfffff56dc580 type <integer_type >>>> 0xfffff57c05e8 int> >>>> side-effects arg:0 <parm_decl 0xfffff76b7000 n2>>>> >>>> arg:1 <integer_cst 0xfffff56c10c8 type <integer_type 0xfffff57c0000 >>>> sizetype> constant 4>> >>>> >>>> >>>> Without unshare_expr to this size_exp, the above TYPE_SIZE_UNIT node >>>> containing SAVE_EXPRs >>>> is gimpflified to expressions with SSA_NAME during gimplification. (This >>>> is unaccepted by LTO). >>>> >>>> Adding an unshare_expr (size_exp) resolved this problem. >>>> >>>> Although I still think that there might be potential issue with the >>>> gimpflication of SAVE_EXPRs, I dare not >>>> to modify that part of the code. >>>> >>>> At this moment, I will add unshare_expr to the routine “pointer_int_sum” >>>> to workaround this issue. >>> >>> It's only a workaround mind you. The bug is that the frontend fails >>> to emit a DECL_EXPR which would >>> trigger both unsharing and proper gimplification of the type size. >> >> For a simple testing case: >> >> $ cat test.c >> struct annotated { >> unsigned int foo; >> char b; >> int array[] __attribute__((counted_by (foo))); >> }; >> extern struct annotated * alloc_buf (int index); >> >> static void bar () >> { >> struct annotated *p2 = alloc_buf (10); >> p2->array[11] = 0; >> return; >> } >> >> The C FE generates the following IR: >> >> [opc@qinzhao-ol8u3-x86 108896]$ cat test.c.005t.original >> ;; Function bar (null) >> ;; enabled by -tree-original >> >> >> { >> struct annotated * p2 = alloc_buf (10); >> >> struct annotated * p2 = alloc_buf (10); >> *(.ACCESS_WITH_SIZE ((int *) &p2->array, &p2->foo, 1, 32, -1) + 44) = 0; >> return; >> } >> >> Do you see any obvious IR issue in the above? Do I miss to generate any >> DECL_EXPR in the above IR? > > It's an interesting question - I don't see where the gimplifier would > need to access DECL/TYPE_SIZE
My bad, the above simple example did not expose the LTO error. Just used to show the IR generated for the array access. The LTO error only happens with multi-dimension array, whose inner dimensions are VLA. And the outermost dimension is flexible array member. Like following: void __attribute__((__noinline__)) setup_and_test_vla (int n1, int n2, int m) { struct foo { int n; int p[][n2][n1] __attribute__((counted_by(n))); } *f; f = (struct foo *) malloc (sizeof(struct foo) + m*sizeof(int[n2][n1])); f->n = m; f->p[m][n2][n1]=1; return; } And the IR for the routine “setup_and_test_vla” is: { typedef struct foo struct struct foo; struct foo * f; SAVE_EXPR <n1>;, SAVE_EXPR <n2>;;, { typedef struct foo struct struct foo; }; struct foo * f; f = (struct foo *) malloc (0, SAVE_EXPR <n1>;, SAVE_EXPR <n2>;;, ((long unsigned int) m * (long unsigned int) ((sizetype) SAVE_EXPR <n1> * (sizetype) SAVE_EXPR <n2>) + 1) * 4;); f->n = m; (*(.ACCESS_WITH_SIZE ((int[0:(sizetype) ((long int) SAVE_EXPR <n2> + -1)][0:(sizetype) ((long int) SAVE_EXPR <n1> + -1)] *) &f->p, &f->n, 1, 32, -1) + (sizetype) (((long unsigned int) m * (long unsigned int) ((sizetype) SAVE_EXPR <n1> * (sizetype) SAVE_EXPR <n2>)) * 4)))[n2][n1] = 1; return; } > so the mistake, if any, should be that you need to unshare the size > expressions you are using as > argument to .ACCESS_WITH_SIZE? Hm, I tried that in the beginning, but didn’t work. I will check again. > Mind, you are replacing an ARRAY_REF > with a pointer indirection > as well Yes, this is for resolving a very early gimplification issue as I reported last Nov: https://gcc.gnu.org/pipermail/gcc-patches/2023-November/638793.html Since no-one responded at that time, I fixed the issue by replacing the ARRAY_REF With a pointer indirection: https://gcc.gnu.org/pipermail/gcc-patches/2023-December/639605.html The reason for such change is: return a flexible array member TYPE is not allowed by C language (our gimplification follows this rule), so, we have to return a pointer TYPE instead. > - IMO we shouldn't replace accesses this way but instead make > it possible for analysis to > discover the base/size values? Yes, if keeping the ARRAY_REF, then bound sanitizer instrumentation will be much simpler since the INDEX Information is kept in the TYPE NODE of the ARRAY_REF. However, due to the above reason, we have to replace the ARRAY_REF with a pointer INDIRECT_REF. Such change made the bound sanitizer more difficult due to the INDEX was lost when the ARRAY_REF was Converted to the INDIRECT_REF. I resolved this issue by adding a new argument to .ACCESS_WITH_SIZE to record the INDEX when converting The ARRAY_REF to INDIRECT_REF during C FE. Let me know if you have any comment. Thanks. Qing > >> Thanks. >> >> Qing >> >> >> I compared it with the following testing case without the “counted-by” >> annotation >> and use an user-defined “access_with_size” function, The IR looks like >> exactly the same: >> >> $ cat test_1.c >> struct annotated { >> unsigned int foo; >> char b; >> int array[]; >> }; >> extern struct annotated *alloc_buf (int); >> extern int *access_with_size (int * ref, unsigned int * size, int a, int b, >> int c); >> >> static void bar () >> { >> struct annotated *p2 = alloc_buf (10); >> access_with_size ((int *) &p2->array, &p2->foo, 1, 32, -1)[11] = 0; >> return; >> } >> [opc@qinzhao-ol8u3-x86 108896]$ cat test_1.c.005t.original >> >> ;; Function bar (null) >> ;; enabled by -tree-original >> >> >> { >> struct annotated * p2 = alloc_buf (10); >> >> struct annotated * p2 = alloc_buf (10); >> *(access_with_size ((int *) &p2->array, &p2->foo, 1, 32, -1) + 44) = 0; >> return; >> } >> >> >> >>> >>>> Let me know if you have any comment and suggestion. >>>> >>>> Thanks a lot. >>>> >>>> Qing >>>> >>>>>> >>>>>>> >>>>>>> Qing >>>>>>> >>>>>>>> >>>>>>>>> Thanks a lot for the help. >>>>>>>>> >>>>>>>>> Qing