diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 092783dfe3a..2ee61b3fe50 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,64 @@ 2005-06-27 Jakub Jelinek + * builtin-attrs.def (DEF_ATTR_FOR_INT): Add for 5 and 6. + (DEF_LIST_INT_INT): Add for 4,0, 4,5, 5,0, 5,6. + (ATTR_NOTHROW_NONNULL_4, ATTR_NOTHROW_NONNULL_5): Define. + (ATTR_FORMAT_PRINTF_4_0, ATTR_FORMAT_PRINTF_4_5, + ATTR_FORMAT_PRINTF_5_0, ATTR_FORMAT_PRINTF_5_6): Define. + * builtins.c: Include tree-flow.h. + (expand_builtin_mempcpy, expand_builtin_memmove): Comment fixes. + (expand_builtin_object_size, expand_builtin_memory_chk, + maybe_emit_chk_warning, maybe_emit_sprintf_chk_warning, + compute_object_offset, compute_builtin_object_size, + fold_builtin_object_size): New functions. + (expand_builtin): Handle BUILT_IN_OBJECT_SIZE and BUILT_IN_*_CHK. + (fold_builtin_1): Likewise. Handle BUILT_IN_{,V}{,F}PRINTF + and BUILT_IN_{,F}PRINTF_UNLOCKED. + (fold_builtin_memory_chk, fold_builtin_stxcpy_chk, + fold_builtin_strncpy_chk, fold_builtin_strcat_chk, + fold_builtin_strncat_chk, fold_builtin_sprintf_chk, + fold_builtin_snprintf_chk, fold_builtin_printf, fold_builtin_fprintf): + New functions. + * builtins.def (BUILT_IN_OBJECT_SIZE, BUILT_IN_MEMCPY_CHK, + BUILT_IN_MEMMOVE_CHK, BUILT_IN_MEMPCPY_CHK, BUILT_IN_MEMSET_CHK, + BUILT_IN_STPCPY_CHK, BUILT_IN_STRCAT_CHK, BUILT_IN_STRCPY_CHK, + BUILT_IN_STRNCAT_CHK, BUILT_IN_STRNCPY_CHK, BUILT_IN_SNPRINTF_CHK, + BUILT_IN_SPRINTF_CHK, BUILT_IN_VSNPRINTF_CHK, BUILT_IN_VSPRINTF_CHK, + BUILT_IN_FPRINTF_CHK, BUILT_IN_PRINTF_CHK, BUILT_IN_VFPRINTF_CHK, + BUILT_IN_VPRINTF_CHK): New builtins. + * builtin-types.def (DEF_FUNCTION_TYPE_5, DEF_FUNCTION_TYPE_VAR_4): + Document. + (BT_FN_SIZE_CONST_PTR_INT, BT_FN_INT_INT_CONST_STRING_VALIST_ARG, + BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, BT_FN_PTR_PTR_INT_SIZE_SIZE, + BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, + BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, + BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, + BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, + BT_FN_INT_INT_CONST_STRING_VAR, BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, + BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, + BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR): New types. + * c-common.c (DEF_FUNCTION_TYPE_5, DEF_FUNCTION_TYPE_6, + DEF_FUNCTION_TYPE_VAR_4, DEF_FUNCTION_TYPE_VAR_5): Define. + * Makefile.in (OBJS-common): Add tree-object-size.o. + (tree-object-size.o): Add dependencies. + * tree-pass.h (pass_object_sizes): Add. + * tree-optimize.c (init_tree_optimization_passes): Add + pass_object_sizes. + * tree-object-size.c: New file. + * tree.h (fold_builtin_memory_chk, fold_builtin_stxcpy_chk, + fold_builtin_strncpy_chk, fold_builtin_snprintf_chk, + compute_builtin_object_size, init_object_sizes, fini_object_sizes): + New prototypes. + * tree-ssa-ccp.c (get_strlen): Rename to ... + (get_maxval_strlen): ...this function. Handle also computing of maximum + string length and maximum integral value. + (ccp_fold_builtin): Handle BUILT_IN_*_CHK. Use get_maxval_strlen + instead of get_strlen. Pass CALLEE and ARGLIST variables to the + folding functions instead of computing them again. + (execute_fold_all_builtins): Retry ccp_fold_builtin if a builtin changed + into some other builtin. + * doc/extend.texi (Object Size Checking): Document. + * regrename.c (copy_value): Fix comment. * toplev.c (process_options): Use if (FRAME_GROWS_DOWNWARD) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 628d36ca421..98ace16f4a7 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -956,7 +956,7 @@ OBJS-common = \ rtl-profile.o tree-profile.o rtlhooks.o cfgexpand.o lambda-mat.o \ lambda-trans.o lambda-code.o tree-loop-linear.o tree-ssa-sink.o \ tree-vrp.o tree-stdarg.o tree-cfgcleanup.o tree-ssa-reassoc.o \ - tree-ssa-structalias.o + tree-ssa-structalias.o tree-object-size.o OBJS-md = $(out_object_file) @@ -1945,6 +1945,9 @@ tree-loop-linear.o: tree-loop-linear.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ tree-stdarg.o: tree-stdarg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TREE_H) function.h $(DIAGNOSTIC_H) $(TREE_FLOW_H) tree-pass.h \ tree-stdarg.h $(TARGET_H) langhooks.h +tree-object-size.o: tree-object-size.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ + $(TM_H) $(TREE_H) $(DIAGNOSTIC_H) $(TREE_FLOW_H) tree-pass.h \ + tree-ssa-propagate.h tree-gimple.o : tree-gimple.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(EXPR_H) \ $(RTL_H) $(TREE_GIMPLE_H) $(TM_H) coretypes.h bitmap.h $(GGC_H) \ output.h $(TREE_FLOW_H) diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 34bde745026..46ac70d4c35 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -55,6 +55,8 @@ DEF_ATTR_FOR_INT (1) DEF_ATTR_FOR_INT (2) DEF_ATTR_FOR_INT (3) DEF_ATTR_FOR_INT (4) +DEF_ATTR_FOR_INT (5) +DEF_ATTR_FOR_INT (6) #undef DEF_ATTR_FOR_INT /* Construct a tree for a list of two integers. */ @@ -67,6 +69,10 @@ DEF_LIST_INT_INT (2,0) DEF_LIST_INT_INT (2,3) DEF_LIST_INT_INT (3,0) DEF_LIST_INT_INT (3,4) +DEF_LIST_INT_INT (4,0) +DEF_LIST_INT_INT (4,5) +DEF_LIST_INT_INT (5,0) +DEF_LIST_INT_INT (5,6) #undef DEF_LIST_INT_INT /* Construct trees for identifiers. */ @@ -127,6 +133,12 @@ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_2, ATTR_NONNULL, ATTR_LIST_2, \ /* Nothrow functions whose third parameter is a nonnull pointer. */ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_3, ATTR_NONNULL, ATTR_LIST_3, \ ATTR_NOTHROW_LIST) +/* Nothrow functions whose fourth parameter is a nonnull pointer. */ +DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_4, ATTR_NONNULL, ATTR_LIST_4, \ + ATTR_NOTHROW_LIST) +/* Nothrow functions whose fifth parameter is a nonnull pointer. */ +DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_5, ATTR_NONNULL, ATTR_LIST_5, \ + ATTR_NOTHROW_LIST) /* Nothrow const functions whose pointer parameter(s) are all nonnull. */ DEF_ATTR_TREE_LIST (ATTR_CONST_NOTHROW_NONNULL, ATTR_CONST, ATTR_NULL, \ ATTR_NOTHROW_NONNULL) @@ -149,6 +161,10 @@ DEF_FORMAT_ATTRIBUTE(PRINTF,2,2_0) DEF_FORMAT_ATTRIBUTE(PRINTF,2,2_3) DEF_FORMAT_ATTRIBUTE(PRINTF,3,3_0) DEF_FORMAT_ATTRIBUTE(PRINTF,3,3_4) +DEF_FORMAT_ATTRIBUTE(PRINTF,4,4_0) +DEF_FORMAT_ATTRIBUTE(PRINTF,4,4_5) +DEF_FORMAT_ATTRIBUTE(PRINTF,5,5_0) +DEF_FORMAT_ATTRIBUTE(PRINTF,5,5_6) DEF_FORMAT_ATTRIBUTE(SCANF,1,1_0) DEF_FORMAT_ATTRIBUTE(SCANF,1,1_2) DEF_FORMAT_ATTRIBUTE(SCANF,2,2_0) diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index 4638e29bb9d..3f25cdd4477 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -32,6 +32,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA DEF_FUNCTION_TYPE_2 (ENUM, RETURN, ARG1, ARG2) DEF_FUNCTION_TYPE_3 (ENUM, RETURN, ARG1, ARG2, ARG3) DEF_FUNCTION_TYPE_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) + DEF_FUNCTION_TYPE_5 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) These macros describe function types. ENUM is as above. The RETURN type is one of the enumerals already defined. ARG1, ARG2, @@ -41,6 +42,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA DEF_FUNCTION_TYPE_VAR_1 (ENUM, RETURN, ARG1) DEF_FUNCTION_TYPE_VAR_2 (ENUM, RETURN, ARG1, ARG2) DEF_FUNCTION_TYPE_VAR_3 (ENUM, RETURN, ARG1, ARG2, ARG3) + DEF_FUNCTION_TYPE_VAR_4 (ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) Similar, but for function types that take variable arguments. For example: @@ -252,6 +254,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOU DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTR, BT_VOID, BT_PTR, BT_PTR) DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING, BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING) +DEF_FUNCTION_TYPE_2 (BT_FN_SIZE_CONST_PTR_INT, BT_SIZE, BT_CONST_PTR, BT_INT) DEF_FUNCTION_TYPE_2 (BT_FN_I1_VPTR_I1, BT_I1, BT_VOLATILE_PTR, BT_I1) DEF_FUNCTION_TYPE_2 (BT_FN_I2_VPTR_I2, BT_I2, BT_VOLATILE_PTR, BT_I2) DEF_FUNCTION_TYPE_2 (BT_FN_I4_VPTR_I4, BT_I4, BT_VOLATILE_PTR, BT_I4) @@ -300,6 +303,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_LONGDOUBLE_LONGDOUBLEPTR_LONGDOUBLEPTR, DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_PTR_PTR, BT_VOID, BT_PTR, BT_PTR, BT_PTR) DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING, BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING, BT_PTR_CONST_STRING) +DEF_FUNCTION_TYPE_3 (BT_FN_INT_INT_CONST_STRING_VALIST_ARG, + BT_INT, BT_INT, BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I1_I1, BT_BOOL, BT_VOLATILE_PTR, BT_I1, BT_I1) DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_VPTR_I2_I2, BT_BOOL, BT_VOLATILE_PTR, @@ -319,6 +324,22 @@ DEF_FUNCTION_TYPE_4 (BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG, BT_INT, BT_STRING, BT_SIZE, BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_STRING_SIZE_CONST_STRING_CONST_PTR, BT_SIZE, BT_STRING, BT_SIZE, BT_CONST_STRING, BT_CONST_PTR) +DEF_FUNCTION_TYPE_4 (BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, + BT_PTR, BT_PTR, BT_CONST_PTR, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_4 (BT_FN_PTR_PTR_INT_SIZE_SIZE, + BT_PTR, BT_PTR, BT_INT, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_4 (BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, + BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE, BT_SIZE) +DEF_FUNCTION_TYPE_4 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, + BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING, BT_VALIST_ARG) + +DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, + BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING, + BT_VALIST_ARG) + +DEF_FUNCTION_TYPE_6 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, + BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE, + BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID) DEF_FUNCTION_TYPE_VAR_0 (BT_FN_INT_VAR, BT_INT) @@ -337,11 +358,22 @@ DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_STRING_CONST_STRING_VAR, BT_INT, BT_STRING, BT_CONST_STRING) DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_CONST_STRING_CONST_STRING_VAR, BT_INT, BT_CONST_STRING, BT_CONST_STRING) +DEF_FUNCTION_TYPE_VAR_2 (BT_FN_INT_INT_CONST_STRING_VAR, + BT_INT, BT_INT, BT_CONST_STRING) DEF_FUNCTION_TYPE_VAR_3 (BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, BT_INT, BT_STRING, BT_SIZE, BT_CONST_STRING) DEF_FUNCTION_TYPE_VAR_3 (BT_FN_SSIZE_STRING_SIZE_CONST_STRING_VAR, BT_SSIZE, BT_STRING, BT_SIZE, BT_CONST_STRING) +DEF_FUNCTION_TYPE_VAR_3 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, + BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING) + +DEF_FUNCTION_TYPE_VAR_4 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, + BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING) + +DEF_FUNCTION_TYPE_VAR_5 (BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, + BT_INT, BT_STRING, BT_SIZE, BT_INT, BT_SIZE, + BT_CONST_STRING) DEF_POINTER_TYPE (BT_PTR_FN_VOID_VAR, BT_FN_VOID_VAR) DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_FN_VOID_VAR_PTR_SIZE, diff --git a/gcc/builtins.c b/gcc/builtins.c index c966c0e0da3..313170bbfbb 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -188,6 +188,18 @@ static tree fold_builtin_strspn (tree); static tree fold_builtin_strcspn (tree); static tree fold_builtin_sprintf (tree, int); +static rtx expand_builtin_object_size (tree); +static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, + enum built_in_function); +static void maybe_emit_chk_warning (tree, enum built_in_function); +static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); +static tree fold_builtin_object_size (tree); +static tree fold_builtin_strcat_chk (tree, tree); +static tree fold_builtin_strncat_chk (tree, tree); +static tree fold_builtin_sprintf_chk (tree, enum built_in_function); +static tree fold_builtin_printf (tree, tree, bool, enum built_in_function); +static tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function); + /* Return true if NODE should be considered for inline expansion regardless of the optimization level. This means whenever a function is invoked with its "internal" name, which normally contains the prefix "__builtin". */ @@ -2821,7 +2833,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode) } /* Expand a call to the mempcpy builtin, with arguments in ARGLIST. - Return 0 if we failed the caller should emit a normal call, + Return 0 if we failed; the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in mode MODE if that's convenient). If ENDP is 0 return the destination pointer, if ENDP is 1 return the end pointer ala @@ -2912,7 +2924,7 @@ expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode m } /* Expand expression EXP, which is a call to the memmove builtin. Return 0 - if we failed the caller should emit a normal call. */ + if we failed; the caller should emit a normal call. */ static rtx expand_builtin_memmove (tree arglist, tree type, rtx target, @@ -6238,6 +6250,32 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_synchronize (); return const0_rtx; + case BUILT_IN_OBJECT_SIZE: + return expand_builtin_object_size (exp); + + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + target = expand_builtin_memory_chk (exp, target, mode, fcode); + if (target) + return target; + break; + + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + maybe_emit_chk_warning (exp, fcode); + break; + + case BUILT_IN_SPRINTF_CHK: + case BUILT_IN_VSPRINTF_CHK: + maybe_emit_sprintf_chk_warning (exp, fcode); + break; + default: /* just do library call, if unknown builtin */ break; } @@ -8787,6 +8825,48 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) case BUILT_IN_VA_START: break; + case BUILT_IN_OBJECT_SIZE: + return fold_builtin_object_size (arglist); + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + return fold_builtin_memory_chk (fndecl, arglist, NULL_TREE, ignore, + DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore, + DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_STRNCPY_CHK: + return fold_builtin_strncpy_chk (arglist, NULL_TREE); + case BUILT_IN_STRCAT_CHK: + return fold_builtin_strcat_chk (fndecl, arglist); + case BUILT_IN_STRNCAT_CHK: + return fold_builtin_strncat_chk (fndecl, arglist); + case BUILT_IN_SPRINTF_CHK: + case BUILT_IN_VSPRINTF_CHK: + return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl)); + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + return fold_builtin_snprintf_chk (arglist, NULL_TREE, + DECL_FUNCTION_CODE (fndecl)); + + case BUILT_IN_PRINTF: + case BUILT_IN_PRINTF_UNLOCKED: + case BUILT_IN_VPRINTF: + case BUILT_IN_PRINTF_CHK: + case BUILT_IN_VPRINTF_CHK: + return fold_builtin_printf (fndecl, arglist, ignore, + DECL_FUNCTION_CODE (fndecl)); + + case BUILT_IN_FPRINTF: + case BUILT_IN_FPRINTF_UNLOCKED: + case BUILT_IN_VFPRINTF: + case BUILT_IN_FPRINTF_CHK: + case BUILT_IN_VFPRINTF_CHK: + return fold_builtin_fprintf (fndecl, arglist, ignore, + DECL_FUNCTION_CODE (fndecl)); + default: break; } @@ -9238,7 +9318,7 @@ fold_builtin_strncat (tree arglist) const char *p = c_getstr (src); /* If the requested length is zero, or the src parameter string - length is zero, return the dst parameter. */ + length is zero, return the dst parameter. */ if (integer_zerop (len) || (p && *p == '\0')) return omit_two_operands (TREE_TYPE (dst), dst, src, len); @@ -9535,7 +9615,7 @@ fold_builtin_sprintf (tree arglist, int ignored) 'sprintf (dest, "%s", orig)'. */ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE) && !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE, - VOID_TYPE)) + VOID_TYPE)) return NULL_TREE; /* Get the destination string and the format specifier. */ @@ -9599,3 +9679,1144 @@ fold_builtin_sprintf (tree arglist, int ignored) else return call; } + +/* Expand a call to __builtin_object_size. */ + +rtx +expand_builtin_object_size (tree exp) +{ + tree ost; + int object_size_type; + tree fndecl = get_callee_fndecl (exp); + tree arglist = TREE_OPERAND (exp, 1); + location_t locus = EXPR_LOCATION (exp); + + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + { + error ("%Hfirst argument of %D must be a pointer, second integer constant", + &locus, fndecl); + expand_builtin_trap (); + return const0_rtx; + } + + ost = TREE_VALUE (TREE_CHAIN (arglist)); + STRIP_NOPS (ost); + + if (TREE_CODE (ost) != INTEGER_CST + || tree_int_cst_sgn (ost) < 0 + || compare_tree_int (ost, 3) > 0) + { + error ("%Hlast argument of %D is not integer constant between 0 and 3", + &locus, fndecl); + expand_builtin_trap (); + return const0_rtx; + } + + object_size_type = tree_low_cst (ost, 0); + + return object_size_type < 2 ? constm1_rtx : const0_rtx; +} + +/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin. + FCODE is the BUILT_IN_* to use. + Return 0 if we failed; the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ + +static rtx +expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode, + enum built_in_function fcode) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, src, len, size; + + if (!validate_arglist (arglist, + POINTER_TYPE, + fcode == BUILT_IN_MEMSET_CHK + ? INTEGER_TYPE : POINTER_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + if (! host_integerp (size, 1)) + return 0; + + if (host_integerp (len, 1) || integer_all_onesp (size)) + { + tree fn; + + if (! integer_all_onesp (size) && tree_int_cst_lt (size, len)) + { + location_t locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + fn = NULL_TREE; + /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume + mem{cpy,pcpy,move,set} is available. */ + switch (fcode) + { + case BUILT_IN_MEMCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMCPY]; + break; + case BUILT_IN_MEMPCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMPCPY]; + break; + case BUILT_IN_MEMMOVE_CHK: + fn = built_in_decls[BUILT_IN_MEMMOVE]; + break; + case BUILT_IN_MEMSET_CHK: + fn = built_in_decls[BUILT_IN_MEMSET]; + break; + default: + break; + } + + if (! fn) + return 0; + + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); + } + else if (fcode == BUILT_IN_MEMSET_CHK) + return 0; + else + { + unsigned int dest_align + = get_pointer_alignment (dest, BIGGEST_ALIGNMENT); + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return 0; + + /* If SRC and DEST are the same (and not volatile), do nothing. */ + if (operand_equal_p (src, dest, 0)) + { + tree expr; + + if (fcode != BUILT_IN_MEMPCPY_CHK) + { + /* Evaluate and ignore LEN in case it has side-effects. */ + expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL); + return expand_expr (dest, target, mode, EXPAND_NORMAL); + } + + len = fold_convert (TREE_TYPE (dest), len); + expr = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len); + return expand_expr (expr, target, mode, EXPAND_NORMAL); + } + + /* __memmove_chk special case. */ + if (fcode == BUILT_IN_MEMMOVE_CHK) + { + unsigned int src_align + = get_pointer_alignment (src, BIGGEST_ALIGNMENT); + + if (src_align == 0) + return 0; + + /* If src is categorized for a readonly section we can use + normal __memcpy_chk. */ + if (readonly_data_expr (src)) + { + tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + fn = build_function_call_expr (fn, arglist); + if (TREE_CODE (fn) == CALL_EXPR) + CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp); + return expand_expr (fn, target, mode, EXPAND_NORMAL); + } + } + return 0; + } +} + +/* Emit warning if a buffer overflow is detected at compile time. */ + +static void +maybe_emit_chk_warning (tree exp, enum built_in_function fcode) +{ + int arg_mask, is_strlen = 0; + tree arglist = TREE_OPERAND (exp, 1), a; + tree len, size; + location_t locus; + + switch (fcode) + { + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + /* For __strcat_chk the warning will be emitted only if overflowing + by at least strlen (dest) + 1 bytes. */ + case BUILT_IN_STRCAT_CHK: + arg_mask = 6; + is_strlen = 1; + break; + case BUILT_IN_STRNCPY_CHK: + arg_mask = 12; + break; + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + arg_mask = 10; + break; + default: + gcc_unreachable (); + } + + len = NULL_TREE; + size = NULL_TREE; + for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1) + if (arg_mask & 1) + { + if (len) + size = a; + else + len = a; + } + + if (!len || !size) + return; + + len = TREE_VALUE (len); + size = TREE_VALUE (size); + + if (! host_integerp (size, 1) || integer_all_onesp (size)) + return; + + if (is_strlen) + { + len = c_strlen (len, 1); + if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size)) + return; + } + else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len)) + return; + + locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); +} + +/* Emit warning if a buffer overflow is detected at compile time + in __sprintf_chk/__vsprintf_chk calls. */ + +static void +maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) +{ + tree arglist = TREE_OPERAND (exp, 1); + tree dest, size, len, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return; + dest = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + flag = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + size = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + if (! arglist) + return; + fmt = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1) || integer_all_onesp (size)) + return; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return; + + /* If the format doesn't contain % args or %%, we know its size. */ + if (strchr (fmt_str, '%') == 0) + len = build_int_cstu (size_type_node, strlen (fmt_str)); + /* If the format is "%s" and first ... argument is a string literal, + we know it too. */ + else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, "%s") == 0) + { + tree arg; + + if (! arglist) + return; + arg = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (arg))) + return; + + len = c_strlen (arg, 1); + if (!len || ! host_integerp (len, 1)) + return; + } + else + return; + + if (! tree_int_cst_lt (len, size)) + { + location_t locus = EXPR_LOCATION (exp); + warning (0, "%Hcall to %D will always overflow destination buffer", + &locus, get_callee_fndecl (exp)); + } +} + +/* Fold a call to __builtin_object_size, if possible. */ + +tree +fold_builtin_object_size (tree arglist) +{ + tree ptr, ost, ret = 0; + int object_size_type; + + if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + ptr = TREE_VALUE (arglist); + ost = TREE_VALUE (TREE_CHAIN (arglist)); + STRIP_NOPS (ost); + + if (TREE_CODE (ost) != INTEGER_CST + || tree_int_cst_sgn (ost) < 0 + || compare_tree_int (ost, 3) > 0) + return 0; + + object_size_type = tree_low_cst (ost, 0); + + /* __builtin_object_size doesn't evaluate side-effects in its arguments; + if there are any side-effects, it returns (size_t) -1 for types 0 and 1 + and (size_t) 0 for types 2 and 3. */ + if (TREE_SIDE_EFFECTS (ptr)) + return fold_convert (size_type_node, + object_size_type < 2 + ? integer_minus_one_node : integer_zero_node); + + if (TREE_CODE (ptr) == ADDR_EXPR) + ret = build_int_cstu (size_type_node, + compute_builtin_object_size (ptr, object_size_type)); + + else if (TREE_CODE (ptr) == SSA_NAME) + { + unsigned HOST_WIDE_INT bytes; + + /* If object size is not known yet, delay folding until + later. Maybe subsequent passes will help determining + it. */ + bytes = compute_builtin_object_size (ptr, object_size_type); + if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 + ? -1 : 0)) + ret = build_int_cstu (size_type_node, bytes); + } + + if (ret) + { + ret = force_fit_type (ret, -1, false, false); + if (TREE_CONSTANT_OVERFLOW (ret)) + ret = 0; + } + + return ret; +} + +/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin. + IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* + code of the builtin. If MAXLEN is not NULL, it is maximum length + passed as third argument. */ + +tree +fold_builtin_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, + enum built_in_function fcode) +{ + tree dest, src, len, size, fn; + + if (!validate_arglist (arglist, + POINTER_TYPE, + fcode == BUILT_IN_MEMSET_CHK + ? INTEGER_TYPE : POINTER_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + /* Actually val for __memset_chk, but it doesn't matter. */ + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + /* If SRC and DEST are the same (and not volatile), return DEST + (resp. DEST+LEN for __mempcpy_chk). */ + if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0)) + { + if (fcode != BUILT_IN_MEMPCPY_CHK) + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); + else + { + tree temp = fold_convert (TREE_TYPE (dest), len); + temp = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), temp); + } + } + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + { + if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) + { + /* (void) __mempcpy_chk () can be optimized into + (void) __memcpy_chk (). */ + fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); + } + return 0; + } + len = maxlen; + } + + if (tree_int_cst_lt (size, len)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + fn = NULL_TREE; + /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume + mem{cpy,pcpy,move,set} is available. */ + switch (fcode) + { + case BUILT_IN_MEMCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMCPY]; + break; + case BUILT_IN_MEMPCPY_CHK: + fn = built_in_decls[BUILT_IN_MEMPCPY]; + break; + case BUILT_IN_MEMMOVE_CHK: + fn = built_in_decls[BUILT_IN_MEMMOVE]; + break; + case BUILT_IN_MEMSET_CHK: + fn = built_in_decls[BUILT_IN_MEMSET]; + break; + default: + break; + } + + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __st[rp]cpy_chk builtin. + IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* + code of the builtin. If MAXLEN is not NULL, it is maximum length of + strings passed as second argument. */ + +tree +fold_builtin_stxcpy_chk (tree fndecl, tree arglist, tree maxlen, bool ignore, + enum built_in_function fcode) +{ + tree dest, src, size, len, fn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + /* If SRC and DEST are the same (and not volatile), return DEST. */ + if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0)) + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + len = c_strlen (src, 1); + if (! len || ! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + { + if (fcode == BUILT_IN_STPCPY_CHK) + { + if (! ignore) + return 0; + + /* If return value of __stpcpy_chk is ignored, + optimize into __strcpy_chk. */ + fn = built_in_decls[BUILT_IN_STRCPY_CHK]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); + } + + if (! len || TREE_SIDE_EFFECTS (len)) + return 0; + + /* If c_strlen returned something, but not a constant, + transform __strcpy_chk into __memcpy_chk. */ + fn = built_in_decls[BUILT_IN_MEMCPY_CHK]; + if (!fn) + return 0; + + len = size_binop (PLUS_EXPR, len, ssize_int (1)); + arglist = build_tree_list (NULL_TREE, size); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), + build_function_call_expr (fn, arglist)); + } + len = maxlen; + } + + if (! tree_int_cst_lt (len, size)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, src); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ + fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK + ? BUILT_IN_STPCPY : BUILT_IN_STRCPY]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strncpy_chk builtin. + If MAXLEN is not NULL, it is maximum length passed as third argument. */ + +tree +fold_builtin_strncpy_chk (tree arglist, tree maxlen) +{ + tree dest, src, size, len, fn; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + return 0; + len = maxlen; + } + + if (tree_int_cst_lt (size, len)) + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strncpy_chk is used, assume strncpy is available. */ + fn = built_in_decls[BUILT_IN_STRNCPY]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strcat_chk builtin FNDECL with ARGLIST. */ + +static tree +fold_builtin_strcat_chk (tree fndecl, tree arglist) +{ + tree dest, src, size, fn; + const char *p; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + + p = c_getstr (src); + /* If the SRC parameter is "", return DEST. */ + if (p && *p == '\0') + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); + + if (! host_integerp (size, 1) || ! integer_all_onesp (size)) + return 0; + + arglist = build_tree_list (NULL_TREE, src); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strcat_chk is used, assume strcat is available. */ + fn = built_in_decls[BUILT_IN_STRCAT]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the __strncat_chk builtin EXP. */ + +static tree +fold_builtin_strncat_chk (tree fndecl, tree arglist) +{ + tree dest, src, size, len, fn; + const char *p; + + if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return 0; + + dest = TREE_VALUE (arglist); + src = TREE_VALUE (TREE_CHAIN (arglist)); + len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); + size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); + + p = c_getstr (src); + /* If the SRC parameter is "" or if LEN is 0, return DEST. */ + if (p && *p == '\0') + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len); + else if (integer_zerop (len)) + return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + tree src_len = c_strlen (src, 1); + if (src_len + && host_integerp (src_len, 1) + && host_integerp (len, 1) + && ! tree_int_cst_lt (len, src_len)) + { + /* If LEN >= strlen (SRC), optimize into __strcat_chk. */ + fn = built_in_decls[BUILT_IN_STRCAT_CHK]; + if (!fn) + return 0; + + arglist = build_tree_list (NULL_TREE, size); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + return build_function_call_expr (fn, arglist); + } + return 0; + } + + arglist = build_tree_list (NULL_TREE, len); + arglist = tree_cons (NULL_TREE, src, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_strncat_chk is used, assume strncat is available. */ + fn = built_in_decls[BUILT_IN_STRNCAT]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */ + +static tree +fold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode) +{ + tree dest, size, len, fn, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (dest))) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + size = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1)) + return 0; + + len = NULL_TREE; + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str != NULL) + { + /* If the format doesn't contain % args or %%, we know the size. */ + if (strchr (fmt_str, '%') == 0) + { + if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE) + len = build_int_cstu (size_type_node, strlen (fmt_str)); + } + /* If the format is "%s" and first ... argument is a string literal, + we know the size too. */ + else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, "%s") == 0) + { + tree arg; + + if (arglist && !TREE_CHAIN (arglist)) + { + arg = TREE_VALUE (arglist); + if (POINTER_TYPE_P (TREE_TYPE (arg))) + { + len = c_strlen (arg, 1); + if (! len || ! host_integerp (len, 1)) + len = NULL_TREE; + } + } + } + } + + if (! integer_all_onesp (size)) + { + if (! len || ! tree_int_cst_lt (len, size)) + return 0; + } + + /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0 + or if format doesn't contain % chars or is "%s". */ + if (! integer_zerop (flag)) + { + if (fmt_str == NULL) + return 0; + if (strchr (fmt_str, '%') != NULL && strcmp (fmt_str, "%s")) + return 0; + } + + arglist = tree_cons (NULL_TREE, fmt, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */ + fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK + ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to {,v}snprintf with argument list ARGLIST. Return 0 if + a normal call should be emitted rather than expanding the function + inline. FCODE is either BUILT_IN_SNPRINTF_CHK or + BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length + passed as second argument. */ + +tree +fold_builtin_snprintf_chk (tree arglist, tree maxlen, + enum built_in_function fcode) +{ + tree dest, size, len, fn, fmt, flag; + const char *fmt_str; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + dest = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (dest))) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + len = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + size = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE) + return 0; + arglist = TREE_CHAIN (arglist); + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (! host_integerp (size, 1)) + return 0; + + if (! integer_all_onesp (size)) + { + if (! host_integerp (len, 1)) + { + /* If LEN is not constant, try MAXLEN too. + For MAXLEN only allow optimizing into non-_ocs function + if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ + if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1)) + return 0; + len = maxlen; + } + + if (tree_int_cst_lt (size, len)) + return 0; + } + + /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0 + or if format doesn't contain % chars or is "%s". */ + if (! integer_zerop (flag)) + { + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return 0; + if (strchr (fmt_str, '%') != NULL && strcmp (fmt_str, "%s")) + return 0; + } + + arglist = tree_cons (NULL_TREE, fmt, arglist); + arglist = tree_cons (NULL_TREE, len, arglist); + arglist = tree_cons (NULL_TREE, dest, arglist); + + /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is + available. */ + fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK + ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF]; + if (!fn) + return 0; + + return build_function_call_expr (fn, arglist); +} + +/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. FCODE is the BUILT_IN_* + code of the function to be simplified. */ + +static tree +fold_builtin_printf (tree fndecl, tree arglist, bool ignore, + enum built_in_function fcode) +{ + tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call; + const char *fmt_str = NULL; + + /* If the return value is used, don't do the transformation. */ + if (! ignore) + return 0; + + /* Verify the required arguments in the original call. */ + if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK) + { + tree flag; + + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE + || TREE_SIDE_EFFECTS (flag)) + return 0; + arglist = TREE_CHAIN (arglist); + } + + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + if (fcode == BUILT_IN_PRINTF_UNLOCKED) + { + fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]; + fn_puts = implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED]; + } + else + { + fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR]; + fn_puts = implicit_built_in_decls[BUILT_IN_PUTS]; + } + + if (strcmp (fmt_str, "%s") == 0 || strchr (fmt_str, '%') == NULL) + { + const char *str; + + if (strcmp (fmt_str, "%s") == 0) + { + if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) + return 0; + + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + + str = c_getstr (TREE_VALUE (arglist)); + if (str == NULL) + return 0; + } + else + { + /* The format specifier doesn't contain any '%' characters. */ + if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK + && arglist) + return 0; + str = fmt_str; + } + + /* If the string was "", printf does nothing. */ + if (str[0] == '\0') + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + + /* If the string has length of 1, call putchar. */ + if (str[1] == '\0') + { + /* Given printf("c"), (where c is any one character,) + convert "c"[0] to an int and pass that to the replacement + function. */ + arg = build_int_cst (NULL_TREE, str[0]); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_putchar; + } + else + { + /* If the string was "string\n", call puts("string"). */ + size_t len = strlen (str); + if (str[len - 1] == '\n') + { + /* Create a NUL-terminated string that's one char shorter + than the original, stripping off the trailing '\n'. */ + char *newstr = alloca (len); + memcpy (newstr, str, len - 1); + newstr[len - 1] = 0; + + arg = build_string_literal (len, newstr); + arglist = build_tree_list (NULL_TREE, arg); + fn = fn_puts; + } + else + /* We'd like to arrange to call fputs(string,stdout) here, + but we need stdout and don't have a way to get it yet. */ + return 0; + } + } + + /* The other optimizations can be done only on the non-va_list variants. */ + else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK) + return 0; + + /* If the format specifier was "%s\n", call __builtin_puts(arg). */ + else if (strcmp (fmt_str, "%s\n") == 0) + { + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + fn = fn_puts; + } + + /* If the format specifier was "%c", call __builtin_putchar(arg). */ + else if (strcmp (fmt_str, "%c") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + fn = fn_putchar; + } + + if (!fn) + return 0; + + call = build_function_call_expr (fn, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); +} + +/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins. + + Return 0 if no simplification was possible, otherwise return the + simplified form of the call as a tree. FCODE is the BUILT_IN_* + code of the function to be simplified. */ + +static tree +fold_builtin_fprintf (tree fndecl, tree arglist, bool ignore, + enum built_in_function fcode) +{ + tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call; + const char *fmt_str = NULL; + + /* If the return value is used, don't do the transformation. */ + if (! ignore) + return 0; + + /* Verify the required arguments in the original call. */ + if (! arglist) + return 0; + fp = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fp))) + return 0; + arglist = TREE_CHAIN (arglist); + + if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK) + { + tree flag; + + if (! arglist) + return 0; + flag = TREE_VALUE (arglist); + if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE + || TREE_SIDE_EFFECTS (flag)) + return 0; + arglist = TREE_CHAIN (arglist); + } + + if (! arglist) + return 0; + fmt = TREE_VALUE (arglist); + if (! POINTER_TYPE_P (TREE_TYPE (fmt))) + return 0; + arglist = TREE_CHAIN (arglist); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return NULL_TREE; + + if (fcode == BUILT_IN_FPRINTF_UNLOCKED) + { + fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]; + fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED]; + } + else + { + fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC]; + fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS]; + } + + /* If the format doesn't contain % args or %%, use strcpy. */ + if (strchr (fmt_str, '%') == NULL) + { + if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK + && arglist) + return 0; + + /* If the format specifier was "", fprintf does nothing. */ + if (fmt_str[0] == '\0') + { + /* If FP has side-effects, just wait until gimplification is + done. */ + if (TREE_SIDE_EFFECTS (fp)) + return 0; + + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0); + } + + /* When "string" doesn't contain %, replace all cases of + fprintf (fp, string) with fputs (string, fp). The fputs + builtin will take care of special cases like length == 1. */ + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, fmt, arglist); + fn = fn_fputs; + } + + /* The other optimizations can be done only on the non-va_list variants. */ + else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK) + return 0; + + /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */ + else if (strcmp (fmt_str, "%s") == 0) + { + if (! arglist + || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputs; + } + + /* If the format specifier was "%c", call __builtin_fputc (arg, fp). */ + else if (strcmp (fmt_str, "%c") == 0) + { + if (! arglist + || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE + || TREE_CHAIN (arglist)) + return 0; + arg = TREE_VALUE (arglist); + arglist = build_tree_list (NULL_TREE, fp); + arglist = tree_cons (NULL_TREE, arg, arglist); + fn = fn_fputc; + } + + if (!fn) + return 0; + + call = build_function_call_expr (fn, arglist); + return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call); +} diff --git a/gcc/builtins.def b/gcc/builtins.def index a5dda5b9a7a..2f5aa151a4f 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -658,6 +658,26 @@ DEF_BUILTIN_STUB (BUILT_IN_NONLOCAL_GOTO, "__builtin_nonlocal_goto") DEF_BUILTIN_STUB (BUILT_IN_STACK_SAVE, "__builtin_stack_save") DEF_BUILTIN_STUB (BUILT_IN_STACK_RESTORE, "__builtin_stack_restore") +/* Object size checking builtins. */ +DEF_GCC_BUILTIN (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMSET_CHK, "__memset_chk", BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY_CHK, "__stpcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCAT_CHK, "__strcat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCPY_CHK, "__strcpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCAT_CHK, "__strncat_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_SNPRINTF_CHK, "__snprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_5_6) +DEF_EXT_LIB_BUILTIN (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_4_5) +DEF_EXT_LIB_BUILTIN (BUILT_IN_VSNPRINTF_CHK, "__vsnprintf_chk", BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_5_0) +DEF_EXT_LIB_BUILTIN (BUILT_IN_VSPRINTF_CHK, "__vsprintf_chk", BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_4_0) +DEF_EXT_LIB_BUILTIN (BUILT_IN_FPRINTF_CHK, "__fprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_3_4) +DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_CHK, "__printf_chk", BT_FN_INT_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_2_3) +DEF_EXT_LIB_BUILTIN (BUILT_IN_VFPRINTF_CHK, "__vfprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_3_0) +DEF_EXT_LIB_BUILTIN (BUILT_IN_VPRINTF_CHK, "__vprintf_chk", BT_FN_INT_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0) + /* Profiling hooks. */ DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter") DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit") diff --git a/gcc/c-common.c b/gcc/c-common.c index 24280aef282..bcf28090554 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -2924,10 +2924,16 @@ c_common_nodes_and_builtins (void) #define DEF_FUNCTION_TYPE_2(NAME, RETURN, ARG1, ARG2) NAME, #define DEF_FUNCTION_TYPE_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME, #define DEF_FUNCTION_TYPE_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME, +#define DEF_FUNCTION_TYPE_5(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) NAME, +#define DEF_FUNCTION_TYPE_6(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) \ + NAME, #define DEF_FUNCTION_TYPE_VAR_0(NAME, RETURN) NAME, #define DEF_FUNCTION_TYPE_VAR_1(NAME, RETURN, ARG1) NAME, #define DEF_FUNCTION_TYPE_VAR_2(NAME, RETURN, ARG1, ARG2) NAME, #define DEF_FUNCTION_TYPE_VAR_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME, +#define DEF_FUNCTION_TYPE_VAR_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME, +#define DEF_FUNCTION_TYPE_VAR_5(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG6) \ + NAME, #define DEF_POINTER_TYPE(NAME, TYPE) NAME, #include "builtin-types.def" #undef DEF_PRIMITIVE_TYPE @@ -2936,10 +2942,14 @@ c_common_nodes_and_builtins (void) #undef DEF_FUNCTION_TYPE_2 #undef DEF_FUNCTION_TYPE_3 #undef DEF_FUNCTION_TYPE_4 +#undef DEF_FUNCTION_TYPE_5 +#undef DEF_FUNCTION_TYPE_6 #undef DEF_FUNCTION_TYPE_VAR_0 #undef DEF_FUNCTION_TYPE_VAR_1 #undef DEF_FUNCTION_TYPE_VAR_2 #undef DEF_FUNCTION_TYPE_VAR_3 +#undef DEF_FUNCTION_TYPE_VAR_4 +#undef DEF_FUNCTION_TYPE_VAR_5 #undef DEF_POINTER_TYPE BT_LAST }; @@ -3188,6 +3198,42 @@ c_common_nodes_and_builtins (void) tree_cons (NULL_TREE, \ builtin_types[(int) ARG4], \ void_list_node))))); +#define DEF_FUNCTION_TYPE_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) \ + builtin_types[(int) ENUM] \ + = build_function_type \ + (builtin_types[(int) RETURN], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG1], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG2], \ + tree_cons \ + (NULL_TREE, \ + builtin_types[(int) ARG3], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG4], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG5],\ + void_list_node)))))); +#define DEF_FUNCTION_TYPE_6(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \ + ARG6) \ + builtin_types[(int) ENUM] \ + = build_function_type \ + (builtin_types[(int) RETURN], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG1], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG2], \ + tree_cons \ + (NULL_TREE, \ + builtin_types[(int) ARG3], \ + tree_cons \ + (NULL_TREE, \ + builtin_types[(int) ARG4], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG5], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG6],\ + void_list_node))))))); #define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN) \ builtin_types[(int) ENUM] \ = build_function_type (builtin_types[(int) RETURN], NULL_TREE); @@ -3220,6 +3266,38 @@ c_common_nodes_and_builtins (void) builtin_types[(int) ARG3], \ NULL_TREE)))); +#define DEF_FUNCTION_TYPE_VAR_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \ + builtin_types[(int) ENUM] \ + = build_function_type \ + (builtin_types[(int) RETURN], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG1], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG2], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG3], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG4],\ + NULL_TREE))))); + +#define DEF_FUNCTION_TYPE_VAR_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, \ + ARG5) \ + builtin_types[(int) ENUM] \ + = build_function_type \ + (builtin_types[(int) RETURN], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG1], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG2], \ + tree_cons \ + (NULL_TREE, \ + builtin_types[(int) ARG3], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG4], \ + tree_cons (NULL_TREE, \ + builtin_types[(int) ARG5],\ + NULL_TREE)))))); + #define DEF_POINTER_TYPE(ENUM, TYPE) \ builtin_types[(int) ENUM] \ = build_pointer_type (builtin_types[(int) TYPE]); @@ -3229,10 +3307,14 @@ c_common_nodes_and_builtins (void) #undef DEF_FUNCTION_TYPE_2 #undef DEF_FUNCTION_TYPE_3 #undef DEF_FUNCTION_TYPE_4 +#undef DEF_FUNCTION_TYPE_5 +#undef DEF_FUNCTION_TYPE_6 #undef DEF_FUNCTION_TYPE_VAR_0 #undef DEF_FUNCTION_TYPE_VAR_1 #undef DEF_FUNCTION_TYPE_VAR_2 #undef DEF_FUNCTION_TYPE_VAR_3 +#undef DEF_FUNCTION_TYPE_VAR_4 +#undef DEF_FUNCTION_TYPE_VAR_5 #undef DEF_POINTER_TYPE c_init_attributes (); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c0363ddbe0f..b019d388219 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -71,6 +71,8 @@ extensions, accepted by GCC in C89 mode and in C++. * Vector Extensions:: Using vector instructions through built-in functions. * Offsetof:: Special syntax for implementing @code{offsetof}. * Atomic Builtins:: Built-in functions for atomic memory access. +* Object Size Checking:: Built-in functions for limited buffer overflow + checking. * Other Builtins:: Other built-in functions. * Target Builtins:: Built-in functions specific to particular targets. * Target Format Checks:: Format checks specific to particular targets. @@ -4724,6 +4726,139 @@ previous memory loads have been satisfied, but following memory reads are not prevented from being speculated to before the barrier. @end table +@node Object Size Checking +@section Object Size Checking Builtins +@findex __builtin_object_size +@findex __builtin___memcpy_chk +@findex __builtin___mempcpy_chk +@findex __builtin___memmove_chk +@findex __builtin___memset_chk +@findex __builtin___strcpy_chk +@findex __builtin___stpcpy_chk +@findex __builtin___strncpy_chk +@findex __builtin___strcat_chk +@findex __builtin___strncat_chk +@findex __builtin___sprintf_chk +@findex __builtin___snprintf_chk +@findex __builtin___vsprintf_chk +@findex __builtin___vsnprintf_chk +@findex __builtin___printf_chk +@findex __builtin___vprintf_chk +@findex __builtin___fprintf_chk +@findex __builtin___vfprintf_chk + +GCC implements a limited buffer overflow protection mechanism +that can prevent some buffer overflow attacks. + +@deftypefn {Built-in Function} {size_t} __builtin_object_size (void * @var{ptr}, int @var{type}) +is a built-in construct that returns a constant number of bytes from +@var{ptr} to the end of the object @var{ptr} pointer points to +(if known at compile time). @code{__builtin_object_size} never evaluates +its arguments for side-effects. If there are any side-effects in them, it +returns @code{(size_t) -1} for @var{type} 0 or 1 and @code{(size_t) 0} +for @var{type} 2 or 3. If there are multiple objects @var{ptr} can +point to and all of them are known at compile time, the returned number +is the maximum of remaining byte counts in those objects if @var{type} & 2 is +0 and minimum if non-zero. If it is not possible to determine which objects +@var{ptr} points to at compile time, @code{__builtin_object_size} should +return @code{(size_t) -1} for @var{type} 0 or 1 and @code{(size_t) 0} +for @var{type} 2 or 3. + +@var{type} is an integer constant from 0 to 3. If the least significant +bit is clear, objects are whole variables, if it is set, a closest +surrounding subobject is considered the object a pointer points to. +The second bit determines if maximum or minimum of remaining bytes +is computed. + +@smallexample +struct V @{ char buf1[10]; int b; char buf2[10]; @} var; +char *p = &var.buf1[1], *q = &var.b; + +/* Here the object p points to is var. */ +assert (__builtin_object_size (p, 0) == sizeof (var) - 1); +/* The subobject p points to is var.buf1. */ +assert (__builtin_object_size (p, 1) == sizeof (var.buf1) - 1); +/* The object q points to is var. */ +assert (__builtin_object_size (q, 0) + == (char *) (&var + 1) - (char *) &var.b); +/* The subobject q points to is var.b. */ +assert (__builtin_object_size (q, 1) == sizeof (var.b)); +@end smallexample +@end deftypefn + +There are built-in functions added for many common string operation +functions, e.g. for @code{memcpy} @code{__builtin___memcpy_chk} +built-in is provided. This built-in has an additional last argument, +which is the number of bytes remaining in object the @var{dest} +argument points to or @code{(size_t) -1} if the size is not known. + +The built-in functions are optimized into the normal string functions +like @code{memcpy} if the last argument is @code{(size_t) -1} or if +it is known at compile time that the destination object will not +be overflown. If the compiler can determine at compile time the +object will be always overflown, it issues a warning. + +The intended use can be e.g. + +@smallexample +#undef memcpy +#define bos0(dest) __builtin_object_size (dest, 0) +#define memcpy(dest, src, n) \ + __builtin___memcpy_chk (dest, src, n, bos0 (dest)) + +char *volatile p; +char buf[10]; +/* It is unknown what object p points to, so this is optimized + into plain memcpy - no checking is possible. */ +memcpy (p, "abcde", n); +/* Destination is known and length too. It is known at compile + time there will be no overflow. */ +memcpy (&buf[5], "abcde", 5); +/* Destination is known, but the length is not known at compile time. + This will result in __memcpy_chk call that can check for overflow + at runtime. */ +memcpy (&buf[5], "abcde", n); +/* Destination is known and it is known at compile time there will + be overflow. There will be a warning and __memcpy_chk call that + will abort the program at runtime. */ +memcpy (&buf[6], "abcde", 5); +@end smallexample + +Such built-in functions are provided for @code{memcpy}, @code{mempcpy}, +@code{memmove}, @code{memset}, @code{strcpy}, @code{stpcpy}, @code{strncpy}, +@code{strcat} and @code{strncat}. + +There are also checking built-in functions for formatted output functions. +@smallexample +int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...); +int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os, + const char *fmt, ...); +int __builtin___vsprintf_chk (char *s, int flag, size_t os, const char *fmt, + va_list ap); +int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os, + const char *fmt, va_list ap); +@end smallexample + +The added @var{flag} argument is passed unchanged to @code{__sprintf_chk} +etc. functions and can contain implementation specific flags on what +additional security measures the checking function might take, such as +handling @code{%n} differently. + +The @var{os} argument is the object size @var{s} points to, like in the +other built-in functions. There is a small difference in the behaviour +though, if @var{os} is @code{(size_t) -1}, the built-in functions are +optimized into the non-checking functions only if @var{flag} is 0, otherwise +the checking function is called with @var{os} argument set to +@code{(size_t) -1}. + +In addition to this, there are checking built-in functions +@code{__builtin___printf_chk}, @code{__builtin___vprintf_chk}, +@code{__builtin___fprintf_chk} and @code{__builtin___vfprintf_chk}. +These have just one additional argument, @var{flag}, right before +format string @var{fmt}. If the compiler is able to optimize them to +@code{fputc} etc. functions, it will, otherwise the checking function +should be called and the @var{flag} argument passed to it. + @node Other Builtins @section Other built-in functions provided by GCC @cindex built-in functions diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c587b1357ed..31444a40c9a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,61 @@ +2005-06-27 Jakub Jelinek + + * gcc.c-torture/execute/builtins/lib/main.c (abort): Add prototype. + * gcc.c-torture/execute/builtins/lib/strncat.c (strncat): Avoid + testing uninitialized var. + + * gcc.c-torture/execute/builtins/chk.h: New. + * gcc.c-torture/execute/builtins/lib/chk.c: New. + * gcc.c-torture/execute/builtins/memcpy-chk.c: New test. + * gcc.c-torture/execute/builtins/memcpy-chk-lib.c: New. + * gcc.c-torture/execute/builtins/memmove-chk.c: New test. + * gcc.c-torture/execute/builtins/memmove-chk-lib.c: New. + * gcc.c-torture/execute/builtins/mempcpy-chk.c: New test. + * gcc.c-torture/execute/builtins/mempcpy-chk-lib.c: New. + * gcc.c-torture/execute/builtins/memset-chk.c: New test. + * gcc.c-torture/execute/builtins/memset-chk-lib.c: New. + * gcc.c-torture/execute/builtins/snprintf-chk.c: New test. + * gcc.c-torture/execute/builtins/snprintf-chk-lib.c: New. + * gcc.c-torture/execute/builtins/sprintf-chk.c: New test. + * gcc.c-torture/execute/builtins/sprintf-chk-lib.c: New. + * gcc.c-torture/execute/builtins/stpcpy-chk.c: New test. + * gcc.c-torture/execute/builtins/stpcpy-chk-lib.c: New. + * gcc.c-torture/execute/builtins/strcat-chk.c: New test. + * gcc.c-torture/execute/builtins/strcat-chk-lib.c: New. + * gcc.c-torture/execute/builtins/strcpy-chk.c: New test. + * gcc.c-torture/execute/builtins/strcpy-chk-lib.c: New. + * gcc.c-torture/execute/builtins/strncat-chk.c: New test. + * gcc.c-torture/execute/builtins/strncat-chk-lib.c: New. + * gcc.c-torture/execute/builtins/strncpy-chk.c: New test. + * gcc.c-torture/execute/builtins/strncpy-chk-lib.c: New. + * gcc.c-torture/execute/builtins/vsnprintf-chk.c: New test. + * gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c: New. + * gcc.c-torture/execute/builtins/vsprintf-chk.c: New test. + * gcc.c-torture/execute/builtins/vsprintf-chk-lib.c: New. + * gcc.dg/builtin-object-size-1.c: New test. + * gcc.dg/builtin-object-size-2.c: New test. + * gcc.dg/builtin-object-size-3.c: New test. + * gcc.dg/builtin-object-size-4.c: New test. + * gcc.dg/builtin-object-size-5.c: New test. + * gcc.dg/builtin-stringop-chk-1.c: New test. + * gcc.dg/builtin-stringop-chk-2.c: New test. + * gcc.dg/tree-ssa/builtin-fprintf-1.c: New test. + * gcc.dg/tree-ssa/builtin-fprintf-chk-1.c: New test. + * gcc.dg/tree-ssa/builtin-printf-1.c: New test. + * gcc.dg/tree-ssa/builtin-printf-chk-1.c: New test. + * gcc.dg/tree-ssa/builtin-vfprintf-1.c: New test. + * gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c: New test. + * gcc.dg/tree-ssa/builtin-vprintf-1.c: New test. + * gcc.dg/tree-ssa/builtin-vprintf-chk-1.c: New test. + * gcc.c-torture/execute/printf-1.c: New test. + * gcc.c-torture/execute/fprintf-1.c: New test. + * gcc.c-torture/execute/vprintf-1.c: New test. + * gcc.c-torture/execute/vfprintf-1.c: New test. + * gcc.c-torture/execute/printf-chk-1.c: New test. + * gcc.c-torture/execute/fprintf-chk-1.c: New test. + * gcc.c-torture/execute/vprintf-chk-1.c: New test. + * gcc.c-torture/execute/vfprintf-chk-1.c: New test. + 2005-06-27 Michael Matz * gcc.target/x86_64/abi/test_struct_returning.c: Adjust as return diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/chk.h b/gcc/testsuite/gcc.c-torture/execute/builtins/chk.h new file mode 100644 index 00000000000..dfef410c61b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/chk.h @@ -0,0 +1,81 @@ +#ifndef os +# define os(ptr) __builtin_object_size (ptr, 0) +#endif + +/* This is one of the alternatives for object size checking. + If dst has side-effects, size checking will never be done. */ +#undef memcpy +#define memcpy(dst, src, len) \ + __builtin___memcpy_chk (dst, src, len, os (dst)) +#undef mempcpy +#define mempcpy(dst, src, len) \ + __builtin___mempcpy_chk (dst, src, len, os (dst)) +#undef memmove +#define memmove(dst, src, len) \ + __builtin___memmove_chk (dst, src, len, os (dst)) +#undef memset +#define memset(dst, val, len) \ + __builtin___memset_chk (dst, val, len, os (dst)) +#undef strcpy +#define strcpy(dst, src) \ + __builtin___strcpy_chk (dst, src, os (dst)) +#undef stpcpy +#define stpcpy(dst, src) \ + __builtin___stpcpy_chk (dst, src, os (dst)) +#undef strcat +#define strcat(dst, src) \ + __builtin___strcat_chk (dst, src, os (dst)) +#undef strncpy +#define strncpy(dst, src, len) \ + __builtin___strncpy_chk (dst, src, len, os (dst)) +#undef strncat +#define strncat(dst, src, len) \ + __builtin___strncat_chk (dst, src, len, os (dst)) +#undef sprintf +#define sprintf(dst, ...) \ + __builtin___sprintf_chk (dst, 0, os (dst), __VA_ARGS__) +#undef vsprintf +#define vsprintf(dst, fmt, ap) \ + __builtin___vsprintf_chk (dst, 0, os (dst), fmt, ap) +#undef snprintf +#define snprintf(dst, len, ...) \ + __builtin___snprintf_chk (dst, len, 0, os (dst), __VA_ARGS__) +#undef vsnprintf +#define vsnprintf(dst, len, fmt, ap) \ + __builtin___vsnprintf_chk (dst, len, 0, os (dst), fmt, ap) + +/* Now "redefine" even builtins for the purpose of testing. */ +#undef __builtin_memcpy +#define __builtin_memcpy(dst, src, len) memcpy (dst, src, len) +#undef __builtin_mempcpy +#define __builtin_mempcpy(dst, src, len) mempcpy (dst, src, len) +#undef __builtin_memmove +#define __builtin_memmove(dst, src, len) memmove (dst, src, len) +#undef __builtin_memset +#define __builtin_memset(dst, val, len) memset (dst, val, len) +#undef __builtin_strcpy +#define __builtin_strcpy(dst, src) strcpy (dst, src) +#undef __builtin_stpcpy +#define __builtin_stpcpy(dst, src) stpcpy (dst, src) +#undef __builtin_strcat +#define __builtin_strcat(dst, src) strcat (dst, src) +#undef __builtin_strncpy +#define __builtin_strncpy(dst, src, len) strncpy (dst, src, len) +#undef __builtin_strncat +#define __builtin_strncat(dst, src, len) strncat (dst, src, len) +#undef __builtin_sprintf +#define __builtin_sprintf(dst, ...) sprintf (dst, __VA_ARGS__) +#undef __builtin_vsprintf +#define __builtin_vsprintf(dst, fmt, ap) vsprintf (dst, fmt, ap) +#undef __builtin_snprintf +#define __builtin_snprintf(dst, len, ...) snprintf (dst, len, __VA_ARGS__) +#undef __builtin_vsnprintf +#define __builtin_vsnprintf(dst, len, fmt, ap) vsnprintf (dst, len, fmt, ap) + +extern void *chk_fail_buf[]; +extern volatile int chk_fail_allowed, chk_calls; +extern volatile int memcpy_disallowed, mempcpy_disallowed, memmove_disallowed; +extern volatile int memset_disallowed, strcpy_disallowed, stpcpy_disallowed; +extern volatile int strncpy_disallowed, strcat_disallowed, strncat_disallowed; +extern volatile int sprintf_disallowed, vsprintf_disallowed; +extern volatile int snprintf_disallowed, vsnprintf_disallowed; diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c new file mode 100644 index 00000000000..eb305d47229 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c @@ -0,0 +1,472 @@ +#include + +extern void abort (void); + +extern int inside_main; +void *chk_fail_buf[256] __attribute__((aligned (16))); +volatile int chk_fail_allowed, chk_calls; +volatile int memcpy_disallowed, mempcpy_disallowed, memmove_disallowed; +volatile int memset_disallowed, strcpy_disallowed, stpcpy_disallowed; +volatile int strncpy_disallowed, strcat_disallowed, strncat_disallowed; +volatile int sprintf_disallowed, vsprintf_disallowed; +volatile int snprintf_disallowed, vsnprintf_disallowed; +extern __SIZE_TYPE__ strlen (const char *); +extern int vsprintf (char *, const char *, va_list); + +void __attribute__((noreturn)) +__chk_fail (void) +{ + if (chk_fail_allowed) + __builtin_longjmp (chk_fail_buf, 1); + abort (); +} + +void * +memcpy (void *dst, const void *src, __SIZE_TYPE__ n) +{ + const char *srcp; + char *dstp; + +#ifdef __OPTIMIZE__ + if (memcpy_disallowed && inside_main) + abort (); +#endif + + srcp = src; + dstp = dst; + while (n-- != 0) + *dstp++ = *srcp++; + + return dst; +} + +void * +__memcpy_chk (void *dst, const void *src, __SIZE_TYPE__ n, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into memcpy. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (n > size) + __chk_fail (); + return memcpy (dst, src, n); +} + +void * +mempcpy (void *dst, const void *src, __SIZE_TYPE__ n) +{ + const char *srcp; + char *dstp; + +#ifdef __OPTIMIZE__ + if (mempcpy_disallowed && inside_main) + abort (); +#endif + + srcp = src; + dstp = dst; + while (n-- != 0) + *dstp++ = *srcp++; + + return dstp; +} + +void * +__mempcpy_chk (void *dst, const void *src, __SIZE_TYPE__ n, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into mempcpy. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (n > size) + __chk_fail (); + return mempcpy (dst, src, n); +} + +void * +memmove (void *dst, const void *src, __SIZE_TYPE__ n) +{ + const char *srcp; + char *dstp; + +#ifdef __OPTIMIZE__ + if (memmove_disallowed && inside_main) + abort (); +#endif + + srcp = src; + dstp = dst; + if (srcp < dstp) + while (n-- != 0) + dstp[n] = srcp[n]; + else + while (n-- != 0) + *dstp++ = *srcp++; + + return dst; +} + +void * +__memmove_chk (void *dst, const void *src, __SIZE_TYPE__ n, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into memmove. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (n > size) + __chk_fail (); + return memmove (dst, src, n); +} + +void * +memset (void *dst, int c, __SIZE_TYPE__ n) +{ + /* Single-byte memsets should be done inline when optimisation + is enabled. */ +#ifdef __OPTIMIZE__ + if (memset_disallowed && inside_main && n < 2) + abort (); +#endif + + while (n-- != 0) + n[(char *) dst] = c; + + return dst; +} + +void * +__memset_chk (void *dst, int c, __SIZE_TYPE__ n, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into memset. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (n > size) + __chk_fail (); + return memset (dst, c, n); +} + +char * +strcpy (char *d, const char *s) +{ + char *r = d; +#ifdef __OPTIMIZE__ + if (strcpy_disallowed && inside_main) + abort (); +#endif + while ((*d++ = *s++)); + return r; +} + +char * +__strcpy_chk (char *d, const char *s, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into strcpy. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (strlen (s) >= size) + __chk_fail (); + return strcpy (d, s); +} + +char * +stpcpy (char *dst, const char *src) +{ +#ifdef __OPTIMIZE__ + if (stpcpy_disallowed && inside_main) + abort (); +#endif + + while (*src != 0) + *dst++ = *src++; + + *dst = 0; + return dst; +} + +char * +__stpcpy_chk (char *d, const char *s, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into stpcpy. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (strlen (s) >= size) + __chk_fail (); + return stpcpy (d, s); +} + +char * +strncpy (char *s1, const char *s2, __SIZE_TYPE__ n) +{ + char *dest = s1; +#ifdef __OPTIMIZE__ + if (strncpy_disallowed && inside_main) + abort(); +#endif + for (; *s2 && n; n--) + *s1++ = *s2++; + while (n--) + *s1++ = 0; + return dest; +} + +char * +__strncpy_chk (char *s1, const char *s2, __SIZE_TYPE__ n, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into strncpy. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (n > size) + __chk_fail (); + return strncpy (s1, s2, n); +} + +char * +strcat (char *dst, const char *src) +{ + char *p = dst; + +#ifdef __OPTIMIZE__ + if (strcat_disallowed && inside_main) + abort (); +#endif + + while (*p) + p++; + while ((*p++ = *src++)) + ; + return dst; +} + +char * +__strcat_chk (char *d, const char *s, __SIZE_TYPE__ size) +{ + /* If size is -1, GCC should always optimize the call into strcat. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + if (strlen (d) + strlen (s) >= size) + __chk_fail (); + return strcat (d, s); +} + +char * +strncat (char *s1, const char *s2, __SIZE_TYPE__ n) +{ + char *dest = s1; + char c; +#ifdef __OPTIMIZE__ + if (strncat_disallowed && inside_main) + abort(); +#endif + while (*s1) s1++; + c = '\0'; + while (n > 0) + { + c = *s2++; + *s1++ = c; + if (c == '\0') + return dest; + n--; + } + if (c != '\0') + *s1 = '\0'; + return dest; +} + +char * +__strncat_chk (char *d, const char *s, __SIZE_TYPE__ n, __SIZE_TYPE__ size) +{ + __SIZE_TYPE__ len = strlen (d), n1 = n; + const char *s1 = s; + + /* If size is -1, GCC should always optimize the call into strncat. */ + if (size == (__SIZE_TYPE__) -1) + abort (); + ++chk_calls; + while (len < size && n1 > 0) + { + if (*s1++ == '\0') + break; + ++len; + --n1; + } + + if (len >= size) + __chk_fail (); + return strncat (d, s, n); +} + +/* No chk test in GCC testsuite needs more bytes than this. + As we can't expect vsnprintf to be available on the target, + assume 4096 bytes is enough. */ +static char chk_sprintf_buf[4096]; + +int +__sprintf_chk (char *str, int flag, __SIZE_TYPE__ size, const char *fmt, ...) +{ + int ret; + va_list ap; + + /* If size is -1 and flag 0, GCC should always optimize the call into + sprintf. */ + if (size == (__SIZE_TYPE__) -1 && flag == 0) + abort (); + ++chk_calls; +#ifdef __OPTIMIZE__ + if (sprintf_disallowed && inside_main) + abort(); +#endif + va_start (ap, fmt); + ret = vsprintf (chk_sprintf_buf, fmt, ap); + va_end (ap); + if (ret >= 0) + { + if (ret >= size) + __chk_fail (); + memcpy (str, chk_sprintf_buf, ret + 1); + } + return ret; +} + +int +__vsprintf_chk (char *str, int flag, __SIZE_TYPE__ size, const char *fmt, + va_list ap) +{ + int ret; + + /* If size is -1 and flag 0, GCC should always optimize the call into + vsprintf. */ + if (size == (__SIZE_TYPE__) -1 && flag == 0) + abort (); + ++chk_calls; +#ifdef __OPTIMIZE__ + if (vsprintf_disallowed && inside_main) + abort(); +#endif + ret = vsprintf (chk_sprintf_buf, fmt, ap); + if (ret >= 0) + { + if (ret >= size) + __chk_fail (); + memcpy (str, chk_sprintf_buf, ret + 1); + } + return ret; +} + +int +__snprintf_chk (char *str, __SIZE_TYPE__ len, int flag, __SIZE_TYPE__ size, + const char *fmt, ...) +{ + int ret; + va_list ap; + + /* If size is -1 and flag 0, GCC should always optimize the call into + snprintf. */ + if (size == (__SIZE_TYPE__) -1 && flag == 0) + abort (); + ++chk_calls; + if (size < len) + __chk_fail (); +#ifdef __OPTIMIZE__ + if (snprintf_disallowed && inside_main) + abort(); +#endif + va_start (ap, fmt); + ret = vsprintf (chk_sprintf_buf, fmt, ap); + va_end (ap); + if (ret >= 0) + { + if (ret < len) + memcpy (str, chk_sprintf_buf, ret + 1); + else + { + memcpy (str, chk_sprintf_buf, len - 1); + str[len - 1] = '\0'; + } + } + return ret; +} + +int +__vsnprintf_chk (char *str, __SIZE_TYPE__ len, int flag, __SIZE_TYPE__ size, + const char *fmt, va_list ap) +{ + int ret; + + /* If size is -1 and flag 0, GCC should always optimize the call into + vsnprintf. */ + if (size == (__SIZE_TYPE__) -1 && flag == 0) + abort (); + ++chk_calls; + if (size < len) + __chk_fail (); +#ifdef __OPTIMIZE__ + if (vsnprintf_disallowed && inside_main) + abort(); +#endif + ret = vsprintf (chk_sprintf_buf, fmt, ap); + if (ret >= 0) + { + if (ret < len) + memcpy (str, chk_sprintf_buf, ret + 1); + else + { + memcpy (str, chk_sprintf_buf, len - 1); + str[len - 1] = '\0'; + } + } + return ret; +} + +int +snprintf (char *str, __SIZE_TYPE__ len, const char *fmt, ...) +{ + int ret; + va_list ap; + +#ifdef __OPTIMIZE__ + if (snprintf_disallowed && inside_main) + abort(); +#endif + va_start (ap, fmt); + ret = vsprintf (chk_sprintf_buf, fmt, ap); + va_end (ap); + if (ret >= 0) + { + if (ret < len) + memcpy (str, chk_sprintf_buf, ret + 1); + else if (len) + { + memcpy (str, chk_sprintf_buf, len - 1); + str[len - 1] = '\0'; + } + } + return ret; +} + +int +vsnprintf (char *str, __SIZE_TYPE__ len, const char *fmt, va_list ap) +{ + int ret; + +#ifdef __OPTIMIZE__ + if (vsnprintf_disallowed && inside_main) + abort(); +#endif + ret = vsprintf (chk_sprintf_buf, fmt, ap); + if (ret >= 0) + { + if (ret < len) + memcpy (str, chk_sprintf_buf, ret + 1); + else if (len) + { + memcpy (str, chk_sprintf_buf, len - 1); + str[len - 1] = '\0'; + } + } + return ret; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c index 1ca606565a8..a9bb6c6b890 100644 --- a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/main.c @@ -1,5 +1,6 @@ extern void abort(void); extern void main_test (void); +extern void abort (void); int inside_main; int diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c index 051dc46c723..290d4cf49bb 100644 --- a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strncat.c @@ -13,11 +13,12 @@ strncat (char *s1, const char *s2, size_t n) abort(); #endif while (*s1) s1++; + c = '\0'; while (n > 0) { c = *s2++; *s1++ = c; - if (c == 0) + if (c == '\0') return dest; n--; } diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c new file mode 100644 index 00000000000..28f7ae785f6 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/memcpy-chk.c @@ -0,0 +1,479 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __memcpy_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern int memcmp (const void *, const void *, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +size_t l1 = 1; + +void +__attribute__((noinline)) +test1 (void) +{ + int i; + +#if defined __i386__ || defined __x86_64__ + /* The functions below might not be optimized into direct stores on all + arches. It depends on how many instructions would be generated and + what limits the architecture chooses in STORE_BY_PIECES_P. */ + memcpy_disallowed = 1; +#endif + + /* All the memcpy calls in this routine except last have fixed length, so + object size checking should be done at compile time if optimizing. */ + chk_calls = 0; + + if (memcpy (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6)) + abort (); + if (memcpy (p + 16, "VWX" + 1, 2) != p + 16 + || memcmp (p + 16, "WX\0\0", 5)) + abort (); + if (memcpy (p + 1, "", 1) != p + 1 || memcmp (p, "A\0CDE", 6)) + abort (); + if (memcpy (p + 3, "FGHI", 4) != p + 3 || memcmp (p, "A\0CFGHI", 8)) + abort (); + + i = 8; + memcpy (p + 20, "qrstu", 6); + memcpy (p + 25, "QRSTU", 6); + if (memcpy (p + 25 + 1, s1, 3) != p + 25 + 1 + || memcmp (p + 25, "Q123U", 6)) + abort (); + + if (memcpy (memcpy (p, "abcdEFG", 4) + 4, "efg", 4) != p + 4 + || memcmp (p, "abcdefg", 8)) + abort(); + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + if (__builtin_memcpy (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6)) + abort (); + + memcpy (p + 5, s3, 1); + if (memcmp (p, "ABCDEFg", 8)) + abort (); + + memcpy_disallowed = 0; + if (chk_calls) + abort (); + chk_calls = 0; + + memcpy (p + 6, s1 + 1, l1); + if (memcmp (p, "ABCDEF2", 8)) + abort (); + + /* The above memcpy copies into an object with known size, but + unknown length, so it should be a __memcpy_chk call. */ + if (chk_calls != 1) + abort (); +} + +long buf1[64]; +char *buf2 = (char *) (buf1 + 32); +long buf5[20]; +char buf7[20]; + +void +__attribute__((noinline)) +test2_sub (long *buf3, char *buf4, char *buf6, int n) +{ + int i = 0; + + /* All the memcpy/__builtin_memcpy/__builtin___memcpy_chk + calls in this routine are either fixed length, or have + side-effects in __builtin_object_size arguments, or + dst doesn't point into a known object. */ + chk_calls = 0; + + /* These should probably be handled by store_by_pieces on most arches. */ + if (memcpy (buf1, "ABCDEFGHI", 9) != (char *) buf1 + || memcmp (buf1, "ABCDEFGHI\0", 11)) + abort (); + + if (memcpy (buf1, "abcdefghijklmnopq", 17) != (char *) buf1 + || memcmp (buf1, "abcdefghijklmnopq\0", 19)) + abort (); + + if (__builtin_memcpy (buf3, "ABCDEF", 6) != (char *) buf1 + || memcmp (buf1, "ABCDEFghijklmnopq\0", 19)) + abort (); + + if (__builtin_memcpy (buf3, "a", 1) != (char *) buf1 + || memcmp (buf1, "aBCDEFghijklmnopq\0", 19)) + abort (); + + if (memcpy ((char *) buf3 + 2, "bcd" + ++i, 2) != (char *) buf1 + 2 + || memcmp (buf1, "aBcdEFghijklmnopq\0", 19) + || i != 1) + abort (); + + /* These should probably be handled by move_by_pieces on most arches. */ + if (memcpy ((char *) buf3 + 4, buf5, 6) != (char *) buf1 + 4 + || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + if (__builtin_memcpy ((char *) buf1 + ++i + 8, (char *) buf5 + 1, 1) + != (char *) buf1 + 10 + || memcmp (buf1, "aBcdRSTUVWSlmnopq\0", 19) + || i != 2) + abort (); + + if (memcpy ((char *) buf3 + 14, buf6, 2) != (char *) buf1 + 14 + || memcmp (buf1, "aBcdRSTUVWSlmnrsq\0", 19)) + abort (); + + if (memcpy (buf3, buf5, 8) != (char *) buf1 + || memcmp (buf1, "RSTUVWXYVWSlmnrsq\0", 19)) + abort (); + + if (memcpy (buf3, buf5, 17) != (char *) buf1 + || memcmp (buf1, "RSTUVWXYZ01234567\0", 19)) + abort (); + + __builtin_memcpy (buf3, "aBcdEFghijklmnopq\0", 19); + + /* These should be handled either by movmemendM or memcpy + call. */ + + /* buf3 points to an unknown object, so __memcpy_chk should not be done. */ + if (memcpy ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 4 + || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* This call has side-effects in dst, therefore no checking. */ + if (__builtin___memcpy_chk ((char *) buf1 + ++i + 8, (char *) buf5 + 1, + n + 1, os ((char *) buf1 + ++i + 8)) + != (char *) buf1 + 11 + || memcmp (buf1, "aBcdRSTUVWkSmnopq\0", 19) + || i != 3) + abort (); + + if (memcpy ((char *) buf3 + 14, buf6, n + 2) != (char *) buf1 + 14 + || memcmp (buf1, "aBcdRSTUVWkSmnrsq\0", 19)) + abort (); + + i = 1; + + /* These might be handled by store_by_pieces. */ + if (memcpy (buf2, "ABCDEFGHI", 9) != buf2 + || memcmp (buf2, "ABCDEFGHI\0", 11)) + abort (); + + if (memcpy (buf2, "abcdefghijklmnopq", 17) != buf2 + || memcmp (buf2, "abcdefghijklmnopq\0", 19)) + abort (); + + if (__builtin_memcpy (buf4, "ABCDEF", 6) != buf2 + || memcmp (buf2, "ABCDEFghijklmnopq\0", 19)) + abort (); + + if (__builtin_memcpy (buf4, "a", 1) != buf2 + || memcmp (buf2, "aBCDEFghijklmnopq\0", 19)) + abort (); + + if (memcpy (buf4 + 2, "bcd" + i++, 2) != buf2 + 2 + || memcmp (buf2, "aBcdEFghijklmnopq\0", 19) + || i != 2) + abort (); + + /* These might be handled by move_by_pieces. */ + if (memcpy (buf4 + 4, buf7, 6) != buf2 + 4 + || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* Side effect. */ + if (__builtin___memcpy_chk (buf2 + i++ + 8, buf7 + 1, 1, + os (buf2 + i++ + 8)) + != buf2 + 10 + || memcmp (buf2, "aBcdRSTUVWSlmnopq\0", 19) + || i != 3) + abort (); + + if (memcpy (buf4 + 14, buf6, 2) != buf2 + 14 + || memcmp (buf2, "aBcdRSTUVWSlmnrsq\0", 19)) + abort (); + + __builtin_memcpy (buf4, "aBcdEFghijklmnopq\0", 19); + + /* These should be handled either by movmemendM or memcpy + call. */ + if (memcpy (buf4 + 4, buf7, n + 6) != buf2 + 4 + || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* Side effect. */ + if (__builtin___memcpy_chk (buf2 + i++ + 8, buf7 + 1, n + 1, + os (buf2 + i++ + 8)) + != buf2 + 11 + || memcmp (buf2, "aBcdRSTUVWkSmnopq\0", 19) + || i != 4) + abort (); + + if (memcpy (buf4 + 14, buf6, n + 2) != buf2 + 14 + || memcmp (buf2, "aBcdRSTUVWkSmnrsq\0", 19)) + abort (); + + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test2 (void) +{ + long *x; + char *y; + int z; + __builtin_memcpy (buf5, "RSTUVWXYZ0123456789", 20); + __builtin_memcpy (buf7, "RSTUVWXYZ0123456789", 20); + __asm ("" : "=r" (x) : "0" (buf1)); + __asm ("" : "=r" (y) : "0" (buf2)); + __asm ("" : "=r" (z) : "0" (0)); + test2_sub (x, y, "rstuvwxyz", z); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + size_t l; + + /* The following calls should do runtime checking + - length is not known, but destination is. */ + chk_calls = 0; + memcpy (a.buf1 + 2, s3, l1); + memcpy (r, s3, l1 + 1); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memcpy (r, s2, l1 + 2); + memcpy (r + 2, s3, l1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + memcpy (r, s2, l1); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + memcpy (a.buf1 + 2, s3, 1); + memcpy (r, s3, 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memcpy (r, s2, 3); + r = buf3; + l = 4; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = 2; + else if (i == l1) + r = &a.buf2[7], l = 3; + else if (i == l1 + 1) + r = &buf3[5], l = 4; + else if (i == l1 + 2) + r = &a.buf1[9], l = 1; + } + memcpy (r, s2, 1); + /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0) + is 4, so this doesn't need runtime checking. */ + memcpy (&buf3[16], s2, l); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test4 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memcpy (&a.buf2[9], s2, l1 + 1); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memcpy (&a.buf2[7], s3, strlen (s3) + 1); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memcpy (&buf3[19], "ab", 2); + abort (); + } + chk_fail_allowed = 0; +} + +#ifndef MAX_OFFSET +#define MAX_OFFSET (sizeof (long long)) +#endif + +#ifndef MAX_COPY +#define MAX_COPY (10 * sizeof (long long)) +#endif + +#ifndef MAX_EXTRA +#define MAX_EXTRA (sizeof (long long)) +#endif + +#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA) + +/* Use a sequence length that is not divisible by two, to make it more + likely to detect when words are mixed up. */ +#define SEQUENCE_LENGTH 31 + +static union { + char buf[MAX_LENGTH]; + long long align_int; + long double align_fp; +} u1, u2; + +void +__attribute__((noinline)) +test5 (void) +{ + int off1, off2, len, i; + char *p, *q, c; + + for (off1 = 0; off1 < MAX_OFFSET; off1++) + for (off2 = 0; off2 < MAX_OFFSET; off2++) + for (len = 1; len < MAX_COPY; len++) + { + for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++) + { + u1.buf[i] = 'a'; + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + u2.buf[i] = c; + } + + p = memcpy (u1.buf + off1, u2.buf + off2, len); + if (p != u1.buf + off1) + abort (); + + q = u1.buf; + for (i = 0; i < off1; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0, c = 'A' + off2; i < len; i++, q++, c++) + { + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + if (*q != c) + abort (); + } + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + } +} + +#define TESTSIZE 80 + +char srcb[TESTSIZE] __attribute__ ((aligned)); +char dstb[TESTSIZE] __attribute__ ((aligned)); + +void +__attribute__((noinline)) +check (char *test, char *match, int n) +{ + if (memcmp (test, match, n)) + abort (); +} + +#define TN(n) \ +{ memset (dstb, 0, n); memcpy (dstb, srcb, n); check (dstb, srcb, n); } +#define T(n) \ +TN (n) \ +TN ((n) + 1) \ +TN ((n) + 2) \ +TN ((n) + 3) + +void +__attribute__((noinline)) +test6 (void) +{ + int i; + + chk_calls = 0; + + for (i = 0; i < sizeof (srcb); ++i) + srcb[i] = 'a' + i % 26; + + T (0); + T (4); + T (8); + T (12); + T (16); + T (20); + T (24); + T (28); + T (32); + T (36); + T (40); + T (44); + T (48); + T (52); + T (56); + T (60); + T (64); + T (68); + T (72); + T (76); + + /* All memcpy calls in this routine have constant arguments. */ + if (chk_calls) + abort (); +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (); + test2 (); + test3 (); + test4 (); + test5 (); + test6 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c new file mode 100644 index 00000000000..12272272ca6 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/memmove-chk.c @@ -0,0 +1,579 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __memcpy_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern void *memmove (void *, const void *, size_t); +extern int memcmp (const void *, const void *, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +size_t l1 = 1; + +void +__attribute__((noinline)) +test1 (void) +{ + int i; + +#if defined __i386__ || defined __x86_64__ + /* The functions below might not be optimized into direct stores on all + arches. It depends on how many instructions would be generated and + what limits the architecture chooses in STORE_BY_PIECES_P. */ + memmove_disallowed = 1; + memcpy_disallowed = 1; +#endif + + /* All the memmove calls in this routine except last have fixed length, so + object size checking should be done at compile time if optimizing. */ + chk_calls = 0; + + if (memmove (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6)) + abort (); + if (memmove (p + 16, "VWX" + 1, 2) != p + 16 + || memcmp (p + 16, "WX\0\0", 5)) + abort (); + if (memmove (p + 1, "", 1) != p + 1 || memcmp (p, "A\0CDE", 6)) + abort (); + if (memmove (p + 3, "FGHI", 4) != p + 3 || memcmp (p, "A\0CFGHI", 8)) + abort (); + + i = 8; + memmove (p + 20, "qrstu", 6); + memmove (p + 25, "QRSTU", 6); + if (memmove (p + 25 + 1, s1, 3) != p + 25 + 1 + || memcmp (p + 25, "Q123U", 6)) + abort (); + + if (memmove (memmove (p, "abcdEFG", 4) + 4, "efg", 4) != p + 4 + || memcmp (p, "abcdefg", 8)) + abort(); + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + if (__builtin_memmove (p, "ABCDE", 6) != p || memcmp (p, "ABCDE", 6)) + abort (); + + memmove (p + 5, s3, 1); + if (memcmp (p, "ABCDEFg", 8)) + abort (); + + memmove_disallowed = 0; + memcpy_disallowed = 0; + if (chk_calls) + abort (); + chk_calls = 0; + + memmove (p + 6, s1 + 1, l1); + if (memcmp (p, "ABCDEF2", 8)) + abort (); + + /* The above memmove copies into an object with known size, but + unknown length, so it should be a __memmove_chk call. */ + if (chk_calls != 1) + abort (); +} + +long buf1[64]; +char *buf2 = (char *) (buf1 + 32); +long buf5[20]; +char buf7[20]; + +void +__attribute__((noinline)) +test2_sub (long *buf3, char *buf4, char *buf6, int n) +{ + int i = 0; + + /* All the memmove/__builtin_memmove/__builtin___memmove_chk + calls in this routine are either fixed length, or have + side-effects in __builtin_object_size arguments, or + dst doesn't point into a known object. */ + chk_calls = 0; + + /* These should probably be handled by store_by_pieces on most arches. */ + if (memmove (buf1, "ABCDEFGHI", 9) != (char *) buf1 + || memcmp (buf1, "ABCDEFGHI\0", 11)) + abort (); + + if (memmove (buf1, "abcdefghijklmnopq", 17) != (char *) buf1 + || memcmp (buf1, "abcdefghijklmnopq\0", 19)) + abort (); + + if (__builtin_memmove (buf3, "ABCDEF", 6) != (char *) buf1 + || memcmp (buf1, "ABCDEFghijklmnopq\0", 19)) + abort (); + + if (__builtin_memmove (buf3, "a", 1) != (char *) buf1 + || memcmp (buf1, "aBCDEFghijklmnopq\0", 19)) + abort (); + + if (memmove ((char *) buf3 + 2, "bcd" + ++i, 2) != (char *) buf1 + 2 + || memcmp (buf1, "aBcdEFghijklmnopq\0", 19) + || i != 1) + abort (); + + /* These should probably be handled by move_by_pieces on most arches. */ + if (memmove ((char *) buf3 + 4, buf5, 6) != (char *) buf1 + 4 + || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + if (__builtin_memmove ((char *) buf1 + ++i + 8, (char *) buf5 + 1, 1) + != (char *) buf1 + 10 + || memcmp (buf1, "aBcdRSTUVWSlmnopq\0", 19) + || i != 2) + abort (); + + if (memmove ((char *) buf3 + 14, buf6, 2) != (char *) buf1 + 14 + || memcmp (buf1, "aBcdRSTUVWSlmnrsq\0", 19)) + abort (); + + if (memmove (buf3, buf5, 8) != (char *) buf1 + || memcmp (buf1, "RSTUVWXYVWSlmnrsq\0", 19)) + abort (); + + if (memmove (buf3, buf5, 17) != (char *) buf1 + || memcmp (buf1, "RSTUVWXYZ01234567\0", 19)) + abort (); + + __builtin_memmove (buf3, "aBcdEFghijklmnopq\0", 19); + + /* These should be handled either by movmemendM or memmove + call. */ + + /* buf3 points to an unknown object, so __memmove_chk should not be done. */ + if (memmove ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 4 + || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* This call has side-effects in dst, therefore no checking. */ + if (__builtin___memmove_chk ((char *) buf1 + ++i + 8, (char *) buf5 + 1, + n + 1, os ((char *) buf1 + ++i + 8)) + != (char *) buf1 + 11 + || memcmp (buf1, "aBcdRSTUVWkSmnopq\0", 19) + || i != 3) + abort (); + + if (memmove ((char *) buf3 + 14, buf6, n + 2) != (char *) buf1 + 14 + || memcmp (buf1, "aBcdRSTUVWkSmnrsq\0", 19)) + abort (); + + i = 1; + + /* These might be handled by store_by_pieces. */ + if (memmove (buf2, "ABCDEFGHI", 9) != buf2 + || memcmp (buf2, "ABCDEFGHI\0", 11)) + abort (); + + if (memmove (buf2, "abcdefghijklmnopq", 17) != buf2 + || memcmp (buf2, "abcdefghijklmnopq\0", 19)) + abort (); + + if (__builtin_memmove (buf4, "ABCDEF", 6) != buf2 + || memcmp (buf2, "ABCDEFghijklmnopq\0", 19)) + abort (); + + if (__builtin_memmove (buf4, "a", 1) != buf2 + || memcmp (buf2, "aBCDEFghijklmnopq\0", 19)) + abort (); + + if (memmove (buf4 + 2, "bcd" + i++, 2) != buf2 + 2 + || memcmp (buf2, "aBcdEFghijklmnopq\0", 19) + || i != 2) + abort (); + + /* These might be handled by move_by_pieces. */ + if (memmove (buf4 + 4, buf7, 6) != buf2 + 4 + || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* Side effect. */ + if (__builtin___memmove_chk (buf2 + i++ + 8, buf7 + 1, 1, + os (buf2 + i++ + 8)) + != buf2 + 10 + || memcmp (buf2, "aBcdRSTUVWSlmnopq\0", 19) + || i != 3) + abort (); + + if (memmove (buf4 + 14, buf6, 2) != buf2 + 14 + || memcmp (buf2, "aBcdRSTUVWSlmnrsq\0", 19)) + abort (); + + __builtin_memmove (buf4, "aBcdEFghijklmnopq\0", 19); + + /* These should be handled either by movmemendM or memmove + call. */ + if (memmove (buf4 + 4, buf7, n + 6) != buf2 + 4 + || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* Side effect. */ + if (__builtin___memmove_chk (buf2 + i++ + 8, buf7 + 1, n + 1, + os (buf2 + i++ + 8)) + != buf2 + 11 + || memcmp (buf2, "aBcdRSTUVWkSmnopq\0", 19) + || i != 4) + abort (); + + if (memmove (buf4 + 14, buf6, n + 2) != buf2 + 14 + || memcmp (buf2, "aBcdRSTUVWkSmnrsq\0", 19)) + abort (); + + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test2 (void) +{ + long *x; + char *y; + int z; + __builtin_memmove (buf5, "RSTUVWXYZ0123456789", 20); + __builtin_memmove (buf7, "RSTUVWXYZ0123456789", 20); + __asm ("" : "=r" (x) : "0" (buf1)); + __asm ("" : "=r" (y) : "0" (buf2)); + __asm ("" : "=r" (z) : "0" (0)); + test2_sub (x, y, "rstuvwxyz", z); +} + +static const struct foo +{ + char *s; + double d; + long l; +} foo[] = +{ + { "hello world1", 3.14159, 101L }, + { "hello world2", 3.14159, 102L }, + { "hello world3", 3.14159, 103L }, + { "hello world4", 3.14159, 104L }, + { "hello world5", 3.14159, 105L }, + { "hello world6", 3.14159, 106L } +}; + +static const struct bar +{ + char *s; + const struct foo f[3]; +} bar[] = +{ + { + "hello world10", + { + { "hello1", 3.14159, 201L }, + { "hello2", 3.14159, 202L }, + { "hello3", 3.14159, 203L }, + } + }, + { + "hello world11", + { + { "hello4", 3.14159, 204L }, + { "hello5", 3.14159, 205L }, + { "hello6", 3.14159, 206L }, + } + } +}; + +static const int baz[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + +void +__attribute__((noinline)) +test3 (void) +{ + const char *s; + struct foo f1[sizeof foo/sizeof*foo]; + struct bar b1[sizeof bar/sizeof*bar]; + int bz[sizeof baz/sizeof*baz]; + + /* All the memmove/__builtin_memmove calls in this routine have fixed + length. */ + chk_calls = 0; + + /* All the *memmove calls below have src in read-only memory, so all + of them should be optimized into memcpy. */ + memmove_disallowed = 1; + if (memmove (f1, foo, sizeof (foo)) != f1 || memcmp (f1, foo, sizeof (foo))) + abort (); + if (memmove (b1, bar, sizeof (bar)) != b1 || memcmp (b1, bar, sizeof (bar))) + abort (); + memmove (bz, baz, sizeof (baz)); + if (memcmp (bz, baz, sizeof (baz))) + abort (); + + if (memmove (p, "abcde", 6) != p || memcmp (p, "abcde", 6)) + abort (); + s = s1; + if (memmove (p + 2, ++s, 0) != p + 2 || memcmp (p, "abcde", 6) || s != s1 + 1) + abort (); + if (__builtin_memmove (p + 3, "", 1) != p + 3 || memcmp (p, "abc\0e", 6)) + abort (); + memmove (p + 2, "fghijk", 4); + if (memcmp (p, "abfghi", 7)) + abort (); + s = s1 + 1; + memmove (p + 1, s++, 0); + if (memcmp (p, "abfghi", 7) || s != s1 + 2) + abort (); + __builtin_memmove (p + 4, "ABCDE", 1); + if (memcmp (p, "abfgAi", 7)) + abort (); + + /* memmove with length 1 can be optimized into memcpy if it can be + expanded inline. */ + if (memmove (p + 2, p + 3, 1) != p + 2) + abort (); + if (memcmp (p, "abggAi", 7)) + abort (); + + if (chk_calls) + abort (); + memmove_disallowed = 0; +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test4 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + size_t l; + + /* The following calls should do runtime checking + - length is not known, but destination is. */ + chk_calls = 0; + memmove (a.buf1 + 2, s3, l1); + memmove (r, s3, l1 + 1); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memmove (r, s2, l1 + 2); + memmove (r + 2, s3, l1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + memmove (r, s2, l1); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + memmove (a.buf1 + 2, s3, 1); + memmove (r, s3, 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memmove (r, s2, 3); + r = buf3; + l = 4; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = 2; + else if (i == l1) + r = &a.buf2[7], l = 3; + else if (i == l1 + 1) + r = &buf3[5], l = 4; + else if (i == l1 + 2) + r = &a.buf1[9], l = 1; + } + memmove (r, s2, 1); + /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0) + is 4, so this doesn't need runtime checking. */ + memmove (&buf3[16], s2, l); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test5 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memmove (&a.buf2[9], s2, l1 + 1); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memmove (&a.buf2[7], s3, strlen (s3) + 1); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memmove (&buf3[19], "ab", 2); + abort (); + } + chk_fail_allowed = 0; +} + +#ifndef MAX_OFFSET +#define MAX_OFFSET (sizeof (long long)) +#endif + +#ifndef MAX_COPY +#define MAX_COPY (10 * sizeof (long long)) +#endif + +#ifndef MAX_EXTRA +#define MAX_EXTRA (sizeof (long long)) +#endif + +#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA) + +/* Use a sequence length that is not divisible by two, to make it more + likely to detect when words are mixed up. */ +#define SEQUENCE_LENGTH 31 + +static union { + char buf[MAX_LENGTH]; + long long align_int; + long double align_fp; +} u1, u2; + +void +__attribute__((noinline)) +test6 (void) +{ + int off1, off2, len, i; + char *p, *q, c; + + for (off1 = 0; off1 < MAX_OFFSET; off1++) + for (off2 = 0; off2 < MAX_OFFSET; off2++) + for (len = 1; len < MAX_COPY; len++) + { + for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++) + { + u1.buf[i] = 'a'; + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + u2.buf[i] = c; + } + + p = memmove (u1.buf + off1, u2.buf + off2, len); + if (p != u1.buf + off1) + abort (); + + q = u1.buf; + for (i = 0; i < off1; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0, c = 'A' + off2; i < len; i++, q++, c++) + { + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + if (*q != c) + abort (); + } + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + } +} + +#define TESTSIZE 80 + +char srcb[TESTSIZE] __attribute__ ((aligned)); +char dstb[TESTSIZE] __attribute__ ((aligned)); + +void +__attribute__((noinline)) +check (char *test, char *match, int n) +{ + if (memcmp (test, match, n)) + abort (); +} + +#define TN(n) \ +{ memset (dstb, 0, n); memmove (dstb, srcb, n); check (dstb, srcb, n); } +#define T(n) \ +TN (n) \ +TN ((n) + 1) \ +TN ((n) + 2) \ +TN ((n) + 3) + +void +__attribute__((noinline)) +test7 (void) +{ + int i; + + chk_calls = 0; + + for (i = 0; i < sizeof (srcb); ++i) + srcb[i] = 'a' + i % 26; + + T (0); + T (4); + T (8); + T (12); + T (16); + T (20); + T (24); + T (28); + T (32); + T (36); + T (40); + T (44); + T (48); + T (52); + T (56); + T (60); + T (64); + T (68); + T (72); + T (76); + + /* All memmove calls in this routine have constant arguments. */ + if (chk_calls) + abort (); +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (); + test2 (); + __builtin_memset (p, '\0', sizeof (p)); + test3 (); + test4 (); + test5 (); + test6 (); + test7 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c new file mode 100644 index 00000000000..a59d59bd85d --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c @@ -0,0 +1,487 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __mempcpy_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern void *mempcpy (void *, const void *, size_t); +extern int memcmp (const void *, const void *, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +size_t l1 = 1; + +void +__attribute__((noinline)) +test1 (void) +{ + int i; + +#if defined __i386__ || defined __x86_64__ + /* The functions below might not be optimized into direct stores on all + arches. It depends on how many instructions would be generated and + what limits the architecture chooses in STORE_BY_PIECES_P. */ + mempcpy_disallowed = 1; +#endif + + /* All the mempcpy calls in this routine except last have fixed length, so + object size checking should be done at compile time if optimizing. */ + chk_calls = 0; + + if (mempcpy (p, "ABCDE", 6) != p + 6 || memcmp (p, "ABCDE", 6)) + abort (); + if (mempcpy (p + 16, "VWX" + 1, 2) != p + 16 + 2 + || memcmp (p + 16, "WX\0\0", 5)) + abort (); + if (mempcpy (p + 1, "", 1) != p + 1 + 1 || memcmp (p, "A\0CDE", 6)) + abort (); + if (mempcpy (p + 3, "FGHI", 4) != p + 3 + 4 || memcmp (p, "A\0CFGHI", 8)) + abort (); + + i = 8; + memcpy (p + 20, "qrstu", 6); + memcpy (p + 25, "QRSTU", 6); + if (mempcpy (p + 25 + 1, s1, 3) != (p + 25 + 1 + 3) + || memcmp (p + 25, "Q123U", 6)) + abort (); + + if (mempcpy (mempcpy (p, "abcdEFG", 4), "efg", 4) != p + 8 + || memcmp (p, "abcdefg", 8)) + abort(); + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + if (__builtin_mempcpy (p, "ABCDE", 6) != p + 6 || memcmp (p, "ABCDE", 6)) + abort (); + + /* If the result of mempcpy is ignored, gcc should use memcpy. + This should be optimized always, so disallow mempcpy calls. */ + mempcpy_disallowed = 1; + mempcpy (p + 5, s3, 1); + if (memcmp (p, "ABCDEFg", 8)) + abort (); + + if (chk_calls) + abort (); + chk_calls = 0; + + mempcpy (p + 6, s1 + 1, l1); + if (memcmp (p, "ABCDEF2", 8)) + abort (); + + /* The above mempcpy copies into an object with known size, but + unknown length and with result ignored, so it should be a + __memcpy_chk call. */ + if (chk_calls != 1) + abort (); + + mempcpy_disallowed = 0; +} + +long buf1[64]; +char *buf2 = (char *) (buf1 + 32); +long buf5[20]; +char buf7[20]; + +void +__attribute__((noinline)) +test2_sub (long *buf3, char *buf4, char *buf6, int n) +{ + int i = 0; + + /* All the mempcpy/__builtin_mempcpy/__builtin___mempcpy_chk + calls in this routine are either fixed length, or have + side-effects in __builtin_object_size arguments, or + dst doesn't point into a known object. */ + chk_calls = 0; + + /* These should probably be handled by store_by_pieces on most arches. */ + if (mempcpy (buf1, "ABCDEFGHI", 9) != (char *) buf1 + 9 + || memcmp (buf1, "ABCDEFGHI\0", 11)) + abort (); + + if (mempcpy (buf1, "abcdefghijklmnopq", 17) != (char *) buf1 + 17 + || memcmp (buf1, "abcdefghijklmnopq\0", 19)) + abort (); + + if (__builtin_mempcpy (buf3, "ABCDEF", 6) != (char *) buf1 + 6 + || memcmp (buf1, "ABCDEFghijklmnopq\0", 19)) + abort (); + + if (__builtin_mempcpy (buf3, "a", 1) != (char *) buf1 + 1 + || memcmp (buf1, "aBCDEFghijklmnopq\0", 19)) + abort (); + + if (mempcpy ((char *) buf3 + 2, "bcd" + ++i, 2) != (char *) buf1 + 4 + || memcmp (buf1, "aBcdEFghijklmnopq\0", 19) + || i != 1) + abort (); + + /* These should probably be handled by move_by_pieces on most arches. */ + if (mempcpy ((char *) buf3 + 4, buf5, 6) != (char *) buf1 + 10 + || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + if (__builtin_mempcpy ((char *) buf1 + ++i + 8, (char *) buf5 + 1, 1) + != (char *) buf1 + 11 + || memcmp (buf1, "aBcdRSTUVWSlmnopq\0", 19) + || i != 2) + abort (); + + if (mempcpy ((char *) buf3 + 14, buf6, 2) != (char *) buf1 + 16 + || memcmp (buf1, "aBcdRSTUVWSlmnrsq\0", 19)) + abort (); + + if (mempcpy (buf3, buf5, 8) != (char *) buf1 + 8 + || memcmp (buf1, "RSTUVWXYVWSlmnrsq\0", 19)) + abort (); + + if (mempcpy (buf3, buf5, 17) != (char *) buf1 + 17 + || memcmp (buf1, "RSTUVWXYZ01234567\0", 19)) + abort (); + + __builtin_memcpy (buf3, "aBcdEFghijklmnopq\0", 19); + + /* These should be handled either by movmemendM or mempcpy + call. */ + + /* buf3 points to an unknown object, so __mempcpy_chk should not be done. */ + if (mempcpy ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 10 + || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* This call has side-effects in dst, therefore no checking. */ + if (__builtin___mempcpy_chk ((char *) buf1 + ++i + 8, (char *) buf5 + 1, + n + 1, os ((char *) buf1 + ++i + 8)) + != (char *) buf1 + 12 + || memcmp (buf1, "aBcdRSTUVWkSmnopq\0", 19) + || i != 3) + abort (); + + if (mempcpy ((char *) buf3 + 14, buf6, n + 2) != (char *) buf1 + 16 + || memcmp (buf1, "aBcdRSTUVWkSmnrsq\0", 19)) + abort (); + + i = 1; + + /* These might be handled by store_by_pieces. */ + if (mempcpy (buf2, "ABCDEFGHI", 9) != buf2 + 9 + || memcmp (buf2, "ABCDEFGHI\0", 11)) + abort (); + + if (mempcpy (buf2, "abcdefghijklmnopq", 17) != buf2 + 17 + || memcmp (buf2, "abcdefghijklmnopq\0", 19)) + abort (); + + if (__builtin_mempcpy (buf4, "ABCDEF", 6) != buf2 + 6 + || memcmp (buf2, "ABCDEFghijklmnopq\0", 19)) + abort (); + + if (__builtin_mempcpy (buf4, "a", 1) != buf2 + 1 + || memcmp (buf2, "aBCDEFghijklmnopq\0", 19)) + abort (); + + if (mempcpy (buf4 + 2, "bcd" + i++, 2) != buf2 + 4 + || memcmp (buf2, "aBcdEFghijklmnopq\0", 19) + || i != 2) + abort (); + + /* These might be handled by move_by_pieces. */ + if (mempcpy (buf4 + 4, buf7, 6) != buf2 + 10 + || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* Side effect. */ + if (__builtin___mempcpy_chk (buf2 + i++ + 8, buf7 + 1, 1, + os (buf2 + i++ + 8)) + != buf2 + 11 + || memcmp (buf2, "aBcdRSTUVWSlmnopq\0", 19) + || i != 3) + abort (); + + if (mempcpy (buf4 + 14, buf6, 2) != buf2 + 16 + || memcmp (buf2, "aBcdRSTUVWSlmnrsq\0", 19)) + abort (); + + __builtin_memcpy (buf4, "aBcdEFghijklmnopq\0", 19); + + /* These should be handled either by movmemendM or mempcpy + call. */ + if (mempcpy (buf4 + 4, buf7, n + 6) != buf2 + 10 + || memcmp (buf2, "aBcdRSTUVWklmnopq\0", 19)) + abort (); + + /* Side effect. */ + if (__builtin___mempcpy_chk (buf2 + i++ + 8, buf7 + 1, + n + 1, os (buf2 + i++ + 8)) + != buf2 + 12 + || memcmp (buf2, "aBcdRSTUVWkSmnopq\0", 19) + || i != 4) + abort (); + + if (mempcpy (buf4 + 14, buf6, n + 2) != buf2 + 16 + || memcmp (buf2, "aBcdRSTUVWkSmnrsq\0", 19)) + abort (); + + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test2 (void) +{ + long *x; + char *y; + int z; + __builtin_memcpy (buf5, "RSTUVWXYZ0123456789", 20); + __builtin_memcpy (buf7, "RSTUVWXYZ0123456789", 20); + __asm ("" : "=r" (x) : "0" (buf1)); + __asm ("" : "=r" (y) : "0" (buf2)); + __asm ("" : "=r" (z) : "0" (0)); + test2_sub (x, y, "rstuvwxyz", z); +} + +volatile void *vx; + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + size_t l; + + /* The following calls should do runtime checking + - length is not known, but destination is. */ + chk_calls = 0; + vx = mempcpy (a.buf1 + 2, s3, l1); + vx = mempcpy (r, s3, l1 + 1); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vx = mempcpy (r, s2, l1 + 2); + vx = mempcpy (r + 2, s3, l1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + vx = mempcpy (r, s2, l1); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + vx = mempcpy (a.buf1 + 2, s3, 1); + vx = mempcpy (r, s3, 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vx = mempcpy (r, s2, 3); + r = buf3; + l = 4; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = 2; + else if (i == l1) + r = &a.buf2[7], l = 3; + else if (i == l1 + 1) + r = &buf3[5], l = 4; + else if (i == l1 + 2) + r = &a.buf1[9], l = 1; + } + vx = mempcpy (r, s2, 1); + /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0) + is 4, so this doesn't need runtime checking. */ + vx = mempcpy (&buf3[16], s2, l); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test4 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + vx = mempcpy (&a.buf2[9], s2, l1 + 1); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + vx = mempcpy (&a.buf2[7], s3, strlen (s3) + 1); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + vx = mempcpy (&buf3[19], "ab", 2); + abort (); + } + chk_fail_allowed = 0; +} + +#ifndef MAX_OFFSET +#define MAX_OFFSET (sizeof (long long)) +#endif + +#ifndef MAX_COPY +#define MAX_COPY (10 * sizeof (long long)) +#endif + +#ifndef MAX_EXTRA +#define MAX_EXTRA (sizeof (long long)) +#endif + +#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA) + +/* Use a sequence length that is not divisible by two, to make it more + likely to detect when words are mixed up. */ +#define SEQUENCE_LENGTH 31 + +static union { + char buf[MAX_LENGTH]; + long long align_int; + long double align_fp; +} u1, u2; + +void +__attribute__((noinline)) +test5 (void) +{ + int off1, off2, len, i; + char *p, *q, c; + + for (off1 = 0; off1 < MAX_OFFSET; off1++) + for (off2 = 0; off2 < MAX_OFFSET; off2++) + for (len = 1; len < MAX_COPY; len++) + { + for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++) + { + u1.buf[i] = 'a'; + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + u2.buf[i] = c; + } + + p = mempcpy (u1.buf + off1, u2.buf + off2, len); + if (p != u1.buf + off1 + len) + abort (); + + q = u1.buf; + for (i = 0; i < off1; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0, c = 'A' + off2; i < len; i++, q++, c++) + { + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + if (*q != c) + abort (); + } + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + } +} + +#define TESTSIZE 80 + +char srcb[TESTSIZE] __attribute__ ((aligned)); +char dstb[TESTSIZE] __attribute__ ((aligned)); + +void +__attribute__((noinline)) +check (char *test, char *match, int n) +{ + if (memcmp (test, match, n)) + abort (); +} + +#define TN(n) \ +{ memset (dstb, 0, n); vx = mempcpy (dstb, srcb, n); check (dstb, srcb, n); } +#define T(n) \ +TN (n) \ +TN ((n) + 1) \ +TN ((n) + 2) \ +TN ((n) + 3) + +void +__attribute__((noinline)) +test6 (void) +{ + int i; + + chk_calls = 0; + + for (i = 0; i < sizeof (srcb); ++i) + srcb[i] = 'a' + i % 26; + + T (0); + T (4); + T (8); + T (12); + T (16); + T (20); + T (24); + T (28); + T (32); + T (36); + T (40); + T (44); + T (48); + T (52); + T (56); + T (60); + T (64); + T (68); + T (72); + T (76); + + /* All mempcpy calls in this routine have constant arguments. */ + if (chk_calls) + abort (); +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (); + test2 (); + test3 (); + test4 (); + test5 (); + test6 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c new file mode 100644 index 00000000000..a8f09a73931 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/memset-chk.c @@ -0,0 +1,721 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __memset_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern int memcmp (const void *, const void *, size_t); + +#include "chk.h" + +char buffer[32]; +int argc = 1; +size_t l1 = 1; +char *s3 = "FGH"; +char *s4; + +void +__attribute__((noinline)) +test1 (void) +{ + memset_disallowed = 1; + chk_calls = 0; + memset (buffer, argc, 0); + memset (buffer, argc, 1); + memset (buffer, argc, 2); + memset (buffer, argc, 3); + memset (buffer, argc, 4); + memset (buffer, argc, 5); + memset (buffer, argc, 6); + memset (buffer, argc, 7); + memset (buffer, argc, 8); + memset (buffer, argc, 9); + memset (buffer, argc, 10); + memset (buffer, argc, 11); + memset (buffer, argc, 12); + memset (buffer, argc, 13); + memset (buffer, argc, 14); + memset (buffer, argc, 15); + memset (buffer, argc, 16); + memset (buffer, argc, 17); + memset_disallowed = 0; + if (chk_calls) + abort (); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + size_t l; + + /* The following calls should do runtime checking + - length is not known, but destination is. */ + chk_calls = 0; + memset (a.buf1 + 2, 'a', l1); + memset (r, '\0', l1 + 1); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memset (r, argc, l1 + 2); + memset (r + 2, 'Q', l1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + memset (r, '\0', l1); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + memset (a.buf1 + 2, '\0', 1); + memset (r, argc, 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memset (r, 'N', 3); + r = buf3; + l = 4; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = 2; + else if (i == l1) + r = &a.buf2[7], l = 3; + else if (i == l1 + 1) + r = &buf3[5], l = 4; + else if (i == l1 + 2) + r = &a.buf1[9], l = 1; + } + memset (r, 'H', 1); + /* Here, l is known to be at most 4 and __builtin_object_size (&buf3[16], 0) + is 4, so this doesn't need runtime checking. */ + memset (&buf3[16], 'd', l); + /* Neither length nor destination known. Doesn't need runtime checking. */ + memset (s4, 'a', l1); + memset (s4 + 2, '\0', l1 + 2); + /* Destination unknown. */ + memset (s4 + 4, 'b', 2); + memset (s4 + 6, '\0', 4); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memset (&a.buf2[9], '\0', l1 + 1); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memset (&a.buf2[7], 'T', strlen (s3) + 1); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + memset (&buf3[19], 'b', 2); + abort (); + } + chk_fail_allowed = 0; +} + +#ifndef MAX_OFFSET +#define MAX_OFFSET (sizeof (long long)) +#endif + +#ifndef MAX_COPY +#define MAX_COPY (10 * sizeof (long long)) +#define MAX_COPY2 15 +#else +#define MAX_COPY2 MAX_COPY +#endif + +#ifndef MAX_EXTRA +#define MAX_EXTRA (sizeof (long long)) +#endif + +#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + MAX_EXTRA) +#define MAX_LENGTH2 (MAX_OFFSET + MAX_COPY2 + MAX_EXTRA) + +static union { + char buf[MAX_LENGTH]; + long long align_int; + long double align_fp; +} u; + +char A = 'A'; + +void +__attribute__((noinline)) +test4 (void) +{ + int off, len, i; + char *p, *q; + + for (off = 0; off < MAX_OFFSET; off++) + for (len = 1; len < MAX_COPY; len++) + { + for (i = 0; i < MAX_LENGTH; i++) + u.buf[i] = 'a'; + + p = memset (u.buf + off, '\0', len); + if (p != u.buf + off) + abort (); + + q = u.buf; + for (i = 0; i < off; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0; i < len; i++, q++) + if (*q != '\0') + abort (); + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + + p = memset (u.buf + off, A, len); + if (p != u.buf + off) + abort (); + + q = u.buf; + for (i = 0; i < off; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0; i < len; i++, q++) + if (*q != 'A') + abort (); + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + + p = memset (u.buf + off, 'B', len); + if (p != u.buf + off) + abort (); + + q = u.buf; + for (i = 0; i < off; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0; i < len; i++, q++) + if (*q != 'B') + abort (); + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + } +} + +static union { + char buf[MAX_LENGTH2]; + long long align_int; + long double align_fp; +} u2; + +void reset () +{ + int i; + + for (i = 0; i < MAX_LENGTH2; i++) + u2.buf[i] = 'a'; +} + +void check (int off, int len, int ch) +{ + char *q; + int i; + + q = u2.buf; + for (i = 0; i < off; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0; i < len; i++, q++) + if (*q != ch) + abort (); + + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); +} + +void +__attribute__((noinline)) +test5 (void) +{ + int off; + char *p; + + /* len == 1 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 1); + if (p != u2.buf + off) abort (); + check (off, 1, '\0'); + + p = memset (u2.buf + off, A, 1); + if (p != u2.buf + off) abort (); + check (off, 1, 'A'); + + p = memset (u2.buf + off, 'B', 1); + if (p != u2.buf + off) abort (); + check (off, 1, 'B'); + } + + /* len == 2 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 2); + if (p != u2.buf + off) abort (); + check (off, 2, '\0'); + + p = memset (u2.buf + off, A, 2); + if (p != u2.buf + off) abort (); + check (off, 2, 'A'); + + p = memset (u2.buf + off, 'B', 2); + if (p != u2.buf + off) abort (); + check (off, 2, 'B'); + } + + /* len == 3 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 3); + if (p != u2.buf + off) abort (); + check (off, 3, '\0'); + + p = memset (u2.buf + off, A, 3); + if (p != u2.buf + off) abort (); + check (off, 3, 'A'); + + p = memset (u2.buf + off, 'B', 3); + if (p != u2.buf + off) abort (); + check (off, 3, 'B'); + } + + /* len == 4 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 4); + if (p != u2.buf + off) abort (); + check (off, 4, '\0'); + + p = memset (u2.buf + off, A, 4); + if (p != u2.buf + off) abort (); + check (off, 4, 'A'); + + p = memset (u2.buf + off, 'B', 4); + if (p != u2.buf + off) abort (); + check (off, 4, 'B'); + } + + /* len == 5 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 5); + if (p != u2.buf + off) abort (); + check (off, 5, '\0'); + + p = memset (u2.buf + off, A, 5); + if (p != u2.buf + off) abort (); + check (off, 5, 'A'); + + p = memset (u2.buf + off, 'B', 5); + if (p != u2.buf + off) abort (); + check (off, 5, 'B'); + } + + /* len == 6 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 6); + if (p != u2.buf + off) abort (); + check (off, 6, '\0'); + + p = memset (u2.buf + off, A, 6); + if (p != u2.buf + off) abort (); + check (off, 6, 'A'); + + p = memset (u2.buf + off, 'B', 6); + if (p != u2.buf + off) abort (); + check (off, 6, 'B'); + } + + /* len == 7 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 7); + if (p != u2.buf + off) abort (); + check (off, 7, '\0'); + + p = memset (u2.buf + off, A, 7); + if (p != u2.buf + off) abort (); + check (off, 7, 'A'); + + p = memset (u2.buf + off, 'B', 7); + if (p != u2.buf + off) abort (); + check (off, 7, 'B'); + } + + /* len == 8 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 8); + if (p != u2.buf + off) abort (); + check (off, 8, '\0'); + + p = memset (u2.buf + off, A, 8); + if (p != u2.buf + off) abort (); + check (off, 8, 'A'); + + p = memset (u2.buf + off, 'B', 8); + if (p != u2.buf + off) abort (); + check (off, 8, 'B'); + } + + /* len == 9 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 9); + if (p != u2.buf + off) abort (); + check (off, 9, '\0'); + + p = memset (u2.buf + off, A, 9); + if (p != u2.buf + off) abort (); + check (off, 9, 'A'); + + p = memset (u2.buf + off, 'B', 9); + if (p != u2.buf + off) abort (); + check (off, 9, 'B'); + } + + /* len == 10 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 10); + if (p != u2.buf + off) abort (); + check (off, 10, '\0'); + + p = memset (u2.buf + off, A, 10); + if (p != u2.buf + off) abort (); + check (off, 10, 'A'); + + p = memset (u2.buf + off, 'B', 10); + if (p != u2.buf + off) abort (); + check (off, 10, 'B'); + } + + /* len == 11 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 11); + if (p != u2.buf + off) abort (); + check (off, 11, '\0'); + + p = memset (u2.buf + off, A, 11); + if (p != u2.buf + off) abort (); + check (off, 11, 'A'); + + p = memset (u2.buf + off, 'B', 11); + if (p != u2.buf + off) abort (); + check (off, 11, 'B'); + } + + /* len == 12 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 12); + if (p != u2.buf + off) abort (); + check (off, 12, '\0'); + + p = memset (u2.buf + off, A, 12); + if (p != u2.buf + off) abort (); + check (off, 12, 'A'); + + p = memset (u2.buf + off, 'B', 12); + if (p != u2.buf + off) abort (); + check (off, 12, 'B'); + } + + /* len == 13 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 13); + if (p != u2.buf + off) abort (); + check (off, 13, '\0'); + + p = memset (u2.buf + off, A, 13); + if (p != u2.buf + off) abort (); + check (off, 13, 'A'); + + p = memset (u2.buf + off, 'B', 13); + if (p != u2.buf + off) abort (); + check (off, 13, 'B'); + } + + /* len == 14 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 14); + if (p != u2.buf + off) abort (); + check (off, 14, '\0'); + + p = memset (u2.buf + off, A, 14); + if (p != u2.buf + off) abort (); + check (off, 14, 'A'); + + p = memset (u2.buf + off, 'B', 14); + if (p != u2.buf + off) abort (); + check (off, 14, 'B'); + } + + /* len == 15 */ + for (off = 0; off < MAX_OFFSET; off++) + { + reset (); + + p = memset (u2.buf + off, '\0', 15); + if (p != u2.buf + off) abort (); + check (off, 15, '\0'); + + p = memset (u2.buf + off, A, 15); + if (p != u2.buf + off) abort (); + check (off, 15, 'A'); + + p = memset (u2.buf + off, 'B', 15); + if (p != u2.buf + off) abort (); + check (off, 15, 'B'); + } +} + +void +__attribute__((noinline)) +test6 (void) +{ + int len; + char *p; + + /* off == 0 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf, '\0', len); + if (p != u2.buf) abort (); + check (0, len, '\0'); + + p = memset (u2.buf, A, len); + if (p != u2.buf) abort (); + check (0, len, 'A'); + + p = memset (u2.buf, 'B', len); + if (p != u2.buf) abort (); + check (0, len, 'B'); + } + + /* off == 1 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+1, '\0', len); + if (p != u2.buf+1) abort (); + check (1, len, '\0'); + + p = memset (u2.buf+1, A, len); + if (p != u2.buf+1) abort (); + check (1, len, 'A'); + + p = memset (u2.buf+1, 'B', len); + if (p != u2.buf+1) abort (); + check (1, len, 'B'); + } + + /* off == 2 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+2, '\0', len); + if (p != u2.buf+2) abort (); + check (2, len, '\0'); + + p = memset (u2.buf+2, A, len); + if (p != u2.buf+2) abort (); + check (2, len, 'A'); + + p = memset (u2.buf+2, 'B', len); + if (p != u2.buf+2) abort (); + check (2, len, 'B'); + } + + /* off == 3 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+3, '\0', len); + if (p != u2.buf+3) abort (); + check (3, len, '\0'); + + p = memset (u2.buf+3, A, len); + if (p != u2.buf+3) abort (); + check (3, len, 'A'); + + p = memset (u2.buf+3, 'B', len); + if (p != u2.buf+3) abort (); + check (3, len, 'B'); + } + + /* off == 4 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+4, '\0', len); + if (p != u2.buf+4) abort (); + check (4, len, '\0'); + + p = memset (u2.buf+4, A, len); + if (p != u2.buf+4) abort (); + check (4, len, 'A'); + + p = memset (u2.buf+4, 'B', len); + if (p != u2.buf+4) abort (); + check (4, len, 'B'); + } + + /* off == 5 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+5, '\0', len); + if (p != u2.buf+5) abort (); + check (5, len, '\0'); + + p = memset (u2.buf+5, A, len); + if (p != u2.buf+5) abort (); + check (5, len, 'A'); + + p = memset (u2.buf+5, 'B', len); + if (p != u2.buf+5) abort (); + check (5, len, 'B'); + } + + /* off == 6 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+6, '\0', len); + if (p != u2.buf+6) abort (); + check (6, len, '\0'); + + p = memset (u2.buf+6, A, len); + if (p != u2.buf+6) abort (); + check (6, len, 'A'); + + p = memset (u2.buf+6, 'B', len); + if (p != u2.buf+6) abort (); + check (6, len, 'B'); + } + + /* off == 7 */ + for (len = 0; len < MAX_COPY2; len++) + { + reset (); + + p = memset (u2.buf+7, '\0', len); + if (p != u2.buf+7) abort (); + check (7, len, '\0'); + + p = memset (u2.buf+7, A, len); + if (p != u2.buf+7) abort (); + check (7, len, 'A'); + + p = memset (u2.buf+7, 'B', len); + if (p != u2.buf+7) abort (); + check (7, len, 'B'); + } +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = buffer; + test1 (); + test2 (); + test3 (); + test4 (); + test5 (); + test6 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c new file mode 100644 index 00000000000..e6ddc086038 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/snprintf-chk.c @@ -0,0 +1,220 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __snprintf_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int memcmp (const void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern int sprintf (char *, const char *, ...); +extern int snprintf (char *, size_t, const char *, ...); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +static char buffer[32]; +char *ptr = "barf"; + +void +__attribute__((noinline)) +test1 (void) +{ + chk_calls = 0; + /* snprintf_disallowed = 1; */ + + memset (buffer, 'A', 32); + snprintf (buffer, 4, "foo"); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (snprintf (buffer, 4, "foo bar") != 7) + abort (); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + snprintf (buffer, 32, "%s", "bar"); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (snprintf (buffer, 21, "%s", "bar") != 3) + abort (); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + snprintf_disallowed = 0; + + memset (buffer, 'A', 32); + if (snprintf (buffer, 4, "%d%d%d", (int) l1, (int) l1 + 1, (int) l1 + 12) + != 4) + abort (); + if (memcmp (buffer, "121", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (snprintf (buffer, 32, "%d%d%d", (int) l1, (int) l1 + 1, (int) l1 + 12) + != 4) + abort (); + if (memcmp (buffer, "1213", 5) || buffer[5] != 'A') + abort (); + + if (chk_calls) + abort (); + + memset (buffer, 'A', 32); + snprintf (buffer, strlen (ptr) + 1, "%s", ptr); + if (memcmp (buffer, "barf", 5) || buffer[5] != 'A') + abort (); + + memset (buffer, 'A', 32); + snprintf (buffer, l1 + 31, "%d - %c", (int) l1 + 27, *ptr); + if (memcmp (buffer, "28 - b\0AAAAA", 12)) + abort (); + + if (chk_calls != 2) + abort (); + chk_calls = 0; + + memset (s4, 'A', 32); + snprintf (s4, l1 + 6, "%d - %c", (int) l1 - 17, ptr[1]); + if (memcmp (s4, "-16 - \0AAA", 10)) + abort (); + if (chk_calls) + abort (); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + + /* The following calls should do runtime checking + - length is not known, but destination is. */ + chk_calls = 0; + snprintf (a.buf1 + 2, l1, "%s", s3 + 3); + snprintf (r, l1 + 4, "%s%c", s3 + 3, s3[3]); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + snprintf (r, strlen (s2) - 2, "%c %s", s2[2], s2 + 4); + snprintf (r + 2, l1, s3 + 3); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + snprintf (r, l1, s2 + 4); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known source length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + /* snprintf_disallowed = 1; */ + snprintf (a.buf1 + 2, 4, ""); + snprintf (r, 1, "a"); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + snprintf (r, 3, "%s", s1 + 1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + snprintf (r, 1, "%s", ""); + snprintf (r, 0, "%s", ""); + snprintf_disallowed = 0; + /* Unknown destination and source, no checking. */ + snprintf (s4, l1 + 31, "%s %d", s3, 0); + if (chk_calls) + abort (); +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + snprintf (&a.buf2[9], l1 + 1, "%c%s", s2[3], s2 + 4); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + snprintf (&a.buf2[7], l1 + 30, "%s%c", s3 + strlen (s3) - 2, *s3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + snprintf (&a.buf2[7], l1 + 3, "%d", (int) l1 + 9999); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + snprintf (&buf3[19], 2, "a"); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + snprintf (&buf3[17], 4, "a"); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + snprintf (&buf3[17], 4, "%s", "abc"); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = p; + test1 (); + test2 (); + test3 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c new file mode 100644 index 00000000000..95d2a9d2826 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/sprintf-chk.c @@ -0,0 +1,197 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __sprintf_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int memcmp (const void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern int sprintf (char *, const char *, ...); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +static char buffer[32]; +char *ptr = "barf"; + +void +__attribute__((noinline)) +test1 (void) +{ + chk_calls = 0; + sprintf_disallowed = 1; + + memset (buffer, 'A', 32); + sprintf (buffer, "foo"); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (sprintf (buffer, "foo") != 3) + abort (); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + sprintf (buffer, "%s", "bar"); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (sprintf (buffer, "%s", "bar") != 3) + abort (); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + if (chk_calls) + abort (); + sprintf_disallowed = 0; + + memset (buffer, 'A', 32); + sprintf (buffer, "%s", ptr); + if (memcmp (buffer, "barf", 5) || buffer[5] != 'A') + abort (); + + memset (buffer, 'A', 32); + sprintf (buffer, "%d - %c", (int) l1 + 27, *ptr); + if (memcmp (buffer, "28 - b\0AAAAA", 12)) + abort (); + + if (chk_calls != 2) + abort (); + chk_calls = 0; + + sprintf (s4, "%d - %c", (int) l1 - 17, ptr[1]); + if (memcmp (s4, "-16 - a", 8)) + abort (); + if (chk_calls) + abort (); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + chk_calls = 0; + sprintf (a.buf1 + 2, "%s", s3 + 3); + sprintf (r, "%s%c", s3 + 3, s3[3]); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + sprintf (r, "%c %s", s2[2], s2 + 4); + sprintf (r + 2, s3 + 3); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + sprintf (r, s2 + 4); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known source length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + sprintf_disallowed = 1; + sprintf (a.buf1 + 2, ""); + sprintf (r, "a"); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + sprintf (r, "%s", s1 + 1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + sprintf (r, "%s", ""); + sprintf_disallowed = 0; + /* Unknown destination and source, no checking. */ + sprintf (s4, "%s %d", s3, 0); + if (chk_calls) + abort (); +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + sprintf (&a.buf2[9], "%c%s", s2[3], s2 + 4); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + sprintf (&a.buf2[7], "%s%c", s3 + strlen (s3) - 2, *s3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + sprintf (&a.buf2[7], "%d", (int) l1 + 9999); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + sprintf (&buf3[19], "a"); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + sprintf (&buf3[17], "%s", "abc"); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = p; + test1 (); + test2 (); + test3 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c new file mode 100644 index 00000000000..b292c0aec87 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/stpcpy-chk.c @@ -0,0 +1,265 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __stpcpy_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *stpcpy (char *, const char *); +extern int memcmp (const void *, const void *, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; + +void +__attribute__((noinline)) +test1 (void) +{ + int i = 8; + +#if defined __i386__ || defined __x86_64__ + /* The functions below might not be optimized into direct stores on all + arches. It depends on how many instructions would be generated and + what limits the architecture chooses in STORE_BY_PIECES_P. */ + stpcpy_disallowed = 1; +#endif + if (stpcpy (p, "abcde") != p + 5 || memcmp (p, "abcde", 6)) + abort (); + if (stpcpy (p + 16, "vwxyz" + 1) != p + 16 + 4 || memcmp (p + 16, "wxyz", 5)) + abort (); + if (stpcpy (p + 1, "") != p + 1 + 0 || memcmp (p, "a\0cde", 6)) + abort (); + if (stpcpy (p + 3, "fghij") != p + 3 + 5 || memcmp (p, "a\0cfghij", 9)) + abort (); + + if (stpcpy ((i++, p + 20 + 1), "23") != (p + 20 + 1 + 2) + || i != 9 || memcmp (p + 19, "z\0""23\0", 5)) + abort (); + + if (stpcpy (stpcpy (p, "ABCD"), "EFG") != p + 7 || memcmp (p, "ABCDEFG", 8)) + abort(); + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + if (__builtin_stpcpy (p, "abcde") != p + 5 || memcmp (p, "abcde", 6)) + abort (); + + /* If return value of stpcpy is ignored, it should be optimized into + strcpy call. */ + stpcpy_disallowed = 1; + stpcpy (p + 1, "abcd"); + stpcpy_disallowed = 0; + if (memcmp (p, "aabcd", 6)) + abort (); + + if (chk_calls) + abort (); + + chk_calls = 0; + strcpy_disallowed = 1; + if (stpcpy (p, s2) != p + 4 || memcmp (p, "defg\0", 6)) + abort (); + strcpy_disallowed = 0; + stpcpy_disallowed = 1; + stpcpy (p + 2, s3); + stpcpy_disallowed = 0; + if (memcmp (p, "deFGH", 6)) + abort (); + if (chk_calls != 2) + abort (); +} + +#ifndef MAX_OFFSET +#define MAX_OFFSET (sizeof (long long)) +#endif + +#ifndef MAX_COPY +#define MAX_COPY (10 * sizeof (long long)) +#endif + +#ifndef MAX_EXTRA +#define MAX_EXTRA (sizeof (long long)) +#endif + +#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + 1 + MAX_EXTRA) + +/* Use a sequence length that is not divisible by two, to make it more + likely to detect when words are mixed up. */ +#define SEQUENCE_LENGTH 31 + +static union { + char buf[MAX_LENGTH]; + long long align_int; + long double align_fp; +} u1, u2; + +volatile char *vx; + +void +__attribute__((noinline)) +test2 (void) +{ + int off1, off2, len, i; + char *p, *q, c; + + for (off1 = 0; off1 < MAX_OFFSET; off1++) + for (off2 = 0; off2 < MAX_OFFSET; off2++) + for (len = 1; len < MAX_COPY; len++) + { + for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++) + { + u1.buf[i] = 'a'; + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + u2.buf[i] = c; + } + u2.buf[off2 + len] = '\0'; + + p = stpcpy (u1.buf + off1, u2.buf + off2); + if (p != u1.buf + off1 + len) + abort (); + + q = u1.buf; + for (i = 0; i < off1; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0, c = 'A' + off2; i < len; i++, q++, c++) + { + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + if (*q != c) + abort (); + } + + if (*q++ != '\0') + abort (); + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + } +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + const char *l; + + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + chk_calls = 0; + vx = stpcpy (a.buf1 + 2, s3 + 3); + vx = stpcpy (r, s3 + 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vx = stpcpy (r, s2 + 2); + vx = stpcpy (r + 2, s3 + 3); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + vx = stpcpy (r, s2 + 4); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known source length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + vx = stpcpy (a.buf1 + 2, ""); + vx = stpcpy (r, "a"); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vx = stpcpy (r, s1 + 1); + r = buf3; + l = "abc"; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = "e"; + else if (i == l1) + r = &a.buf2[7], l = "gh"; + else if (i == l1 + 1) + r = &buf3[5], l = "jkl"; + else if (i == l1 + 2) + r = &a.buf1[9], l = ""; + } + vx = stpcpy (r, ""); + /* Here, strlen (l) + 1 is known to be at most 4 and + __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need + runtime checking. */ + vx = stpcpy (&buf3[16], l); + /* Unknown destination and source, no checking. */ + vx = stpcpy (s4, s3); + stpcpy (s4 + 4, s3); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test4 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + vx = stpcpy (&a.buf2[9], s2 + 3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + vx = stpcpy (&a.buf2[7], s3 + strlen (s3) - 3); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + vx = stpcpy (&buf3[19], "a"); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (); + s4 = p; + test2 (); + test3 (); + test4 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c new file mode 100644 index 00000000000..fea3184e8f5 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strcat-chk.c @@ -0,0 +1,204 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __strcat_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcat (char *, const char *); +extern int memcmp (const void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int strcmp (const char *, const char *); +extern void *memset (void *, int, size_t); +#define RESET_DST_WITH(FILLER) \ + do { memset (dst, 'X', sizeof (dst)); strcpy (dst, (FILLER)); } while (0) + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +char *s5; + +void +__attribute__((noinline)) +test1 (void) +{ + const char *const x1 = "hello world"; + const char *const x2 = ""; + char dst[64], *d2; + + chk_calls = 0; + strcat_disallowed = 1; + /* Following strcat calls should be optimized out at compile time. */ + RESET_DST_WITH (x1); + if (strcat (dst, "") != dst || strcmp (dst, x1)) + abort (); + RESET_DST_WITH (x1); + if (strcat (dst, x2) != dst || strcmp (dst, x1)) + abort (); + RESET_DST_WITH (x1); d2 = dst; + if (strcat (++d2, x2) != dst+1 || d2 != dst+1 || strcmp (dst, x1)) + abort (); + RESET_DST_WITH (x1); d2 = dst; + if (strcat (++d2+5, x2) != dst+6 || d2 != dst+1 || strcmp (dst, x1)) + abort (); + RESET_DST_WITH (x1); d2 = dst; + if (strcat (++d2+5, x1+11) != dst+6 || d2 != dst+1 || strcmp (dst, x1)) + abort (); + if (chk_calls) + abort (); + strcat_disallowed = 0; + + RESET_DST_WITH (x1); + if (strcat (dst, " 1111") != dst + || memcmp (dst, "hello world 1111\0XXX", 20)) + abort (); + + RESET_DST_WITH (x1); + if (strcat (dst+5, " 2222") != dst+5 + || memcmp (dst, "hello world 2222\0XXX", 20)) + abort (); + + RESET_DST_WITH (x1); d2 = dst; + if (strcat (++d2+5, " 3333") != dst+6 || d2 != dst+1 + || memcmp (dst, "hello world 3333\0XXX", 20)) + abort (); + + RESET_DST_WITH (x1); + strcat (strcat (strcat (strcat (strcat (strcat (dst, ": this "), ""), + "is "), "a "), "test"), "."); + if (memcmp (dst, "hello world: this is a test.\0X", 30)) + abort (); + + chk_calls = 0; + strcat_disallowed = 1; + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + RESET_DST_WITH (x1); + if (__builtin_strcat (dst, "") != dst || strcmp (dst, x1)) + abort (); + if (chk_calls) + abort (); + strcat_disallowed = 0; +} + + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + memset (&a, '\0', sizeof (a)); + s5 = (char *) &a; + __asm __volatile ("" : : "r" (s5) : "memory"); + chk_calls = 0; + strcat (a.buf1 + 2, s3 + 3); + strcat (r, s3 + 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memset (r, '\0', 3); + __asm __volatile ("" : : "r" (r) : "memory"); + strcat (r, s2 + 2); + strcat (r + 2, s3 + 3); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + strcat (r, s2 + 4); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known source length, + but we don't know the length of dest string, so runtime checking + is needed too. */ + memset (&a, '\0', sizeof (a)); + chk_calls = 0; + s5 = (char *) &a; + __asm __volatile ("" : : "r" (s5) : "memory"); + strcat (a.buf1 + 2, "a"); + strcat (r, ""); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memset (r, '\0', 3); + __asm __volatile ("" : : "r" (r) : "memory"); + strcat (r, s1 + 1); + if (chk_calls != 2) + abort (); + chk_calls = 0; + /* Unknown destination and source, no checking. */ + strcat (s4, s3); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + memset (&a, '\0', sizeof (a)); + memset (buf3, '\0', sizeof (buf3)); + s5 = (char *) &a; + __asm __volatile ("" : : "r" (s5) : "memory"); + s5 = buf3; + __asm __volatile ("" : : "r" (s5) : "memory"); + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strcat (&a.buf2[9], s2 + 3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strcat (&a.buf2[7], s3 + strlen (s3) - 3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strcat (&buf3[19], "a"); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = p; + test1 (); + memset (p, '\0', sizeof (p)); + test2 (); + test3 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c new file mode 100644 index 00000000000..002dd19500e --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strcpy-chk.c @@ -0,0 +1,234 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __strcpy_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int memcmp (const void *, const void *, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; + +void +__attribute__((noinline)) +test1 (void) +{ + chk_calls = 0; +#ifndef __OPTIMIZE_SIZE__ + strcpy_disallowed = 1; +#else + strcpy_disallowed = 0; +#endif + + if (strcpy (p, "abcde") != p || memcmp (p, "abcde", 6)) + abort (); + if (strcpy (p + 16, "vwxyz" + 1) != p + 16 || memcmp (p + 16, "wxyz", 5)) + abort (); + if (strcpy (p + 1, "") != p + 1 || memcmp (p, "a\0cde", 6)) + abort (); + if (strcpy (p + 3, "fghij") != p + 3 || memcmp (p, "a\0cfghij", 9)) + abort (); + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + if (__builtin_strcpy (p, "abcde") != p || memcmp (p, "abcde", 6)) + abort (); + + strcpy_disallowed = 0; + if (chk_calls) + abort (); +} + +#ifndef MAX_OFFSET +#define MAX_OFFSET (sizeof (long long)) +#endif + +#ifndef MAX_COPY +#define MAX_COPY (10 * sizeof (long long)) +#endif + +#ifndef MAX_EXTRA +#define MAX_EXTRA (sizeof (long long)) +#endif + +#define MAX_LENGTH (MAX_OFFSET + MAX_COPY + 1 + MAX_EXTRA) + +/* Use a sequence length that is not divisible by two, to make it more + likely to detect when words are mixed up. */ +#define SEQUENCE_LENGTH 31 + +static union { + char buf[MAX_LENGTH]; + long long align_int; + long double align_fp; +} u1, u2; + +void +__attribute__((noinline)) +test2 (void) +{ + int off1, off2, len, i; + char *p, *q, c; + + for (off1 = 0; off1 < MAX_OFFSET; off1++) + for (off2 = 0; off2 < MAX_OFFSET; off2++) + for (len = 1; len < MAX_COPY; len++) + { + for (i = 0, c = 'A'; i < MAX_LENGTH; i++, c++) + { + u1.buf[i] = 'a'; + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + u2.buf[i] = c; + } + u2.buf[off2 + len] = '\0'; + + p = strcpy (u1.buf + off1, u2.buf + off2); + if (p != u1.buf + off1) + abort (); + + q = u1.buf; + for (i = 0; i < off1; i++, q++) + if (*q != 'a') + abort (); + + for (i = 0, c = 'A' + off2; i < len; i++, q++, c++) + { + if (c >= 'A' + SEQUENCE_LENGTH) + c = 'A'; + if (*q != c) + abort (); + } + + if (*q++ != '\0') + abort (); + for (i = 0; i < MAX_EXTRA; i++, q++) + if (*q != 'a') + abort (); + } +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + const char *l; + + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + chk_calls = 0; + strcpy (a.buf1 + 2, s3 + 3); + strcpy (r, s3 + 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + strcpy (r, s2 + 2); + strcpy (r + 2, s3 + 3); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + strcpy (r, s2 + 4); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known source length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + strcpy (a.buf1 + 2, ""); + strcpy (r, "a"); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + strcpy (r, s1 + 1); + r = buf3; + l = "abc"; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = "e"; + else if (i == l1) + r = &a.buf2[7], l = "gh"; + else if (i == l1 + 1) + r = &buf3[5], l = "jkl"; + else if (i == l1 + 2) + r = &a.buf1[9], l = ""; + } + strcpy (r, ""); + /* Here, strlen (l) + 1 is known to be at most 4 and + __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need + runtime checking. */ + strcpy (&buf3[16], l); + /* Unknown destination and source, no checking. */ + strcpy (s4, s3); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test4 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strcpy (&a.buf2[9], s2 + 3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strcpy (&a.buf2[7], s3 + strlen (s3) - 3); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strcpy (&buf3[19], "a"); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (); + test2 (); + s4 = p; + test3 (); + test4 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c new file mode 100644 index 00000000000..8904df14aee --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c @@ -0,0 +1,229 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __strncat_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen (const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcat (char *, const char *); +extern char *strncat (char *, const char *, size_t); +extern int memcmp (const void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int strcmp (const char *, const char *); +extern void *memset (void *, int, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +char *s5; +int x = 123; + +void +__attribute__((noinline)) +test1 (void) +{ + const char *const s1 = "hello world"; + const char *const s2 = ""; + const char *s3; + char dst[64], *d2; + + /* Following strncat calls should be all optimized out. */ + chk_calls = 0; + strncat_disallowed = 1; + strcat_disallowed = 1; + strcpy (dst, s1); + if (strncat (dst, "", 100) != dst || strcmp (dst, s1)) + abort (); + strcpy (dst, s1); + if (strncat (dst, s2, 100) != dst || strcmp (dst, s1)) + abort (); + strcpy (dst, s1); d2 = dst; + if (strncat (++d2, s2, 100) != dst+1 || d2 != dst+1 || strcmp (dst, s1)) + abort (); + strcpy (dst, s1); d2 = dst; + if (strncat (++d2+5, s2, 100) != dst+6 || d2 != dst+1 || strcmp (dst, s1)) + abort (); + strcpy (dst, s1); d2 = dst; + if (strncat (++d2+5, s1+11, 100) != dst+6 || d2 != dst+1 || strcmp (dst, s1)) + abort (); + strcpy (dst, s1); d2 = dst; + if (strncat (++d2+5, s1, 0) != dst+6 || d2 != dst+1 || strcmp (dst, s1)) + abort (); + strcpy (dst, s1); d2 = dst; s3 = s1; + if (strncat (++d2+5, ++s3, 0) != dst+6 || d2 != dst+1 || strcmp (dst, s1) + || s3 != s1 + 1) + abort (); + strcpy (dst, s1); d2 = dst; + if (strncat (++d2+5, "", ++x) != dst+6 || d2 != dst+1 || x != 124 + || strcmp (dst, s1)) + abort (); + if (chk_calls) + abort (); + strcat_disallowed = 0; + + /* These __strncat_chk calls should be optimized into __strcat_chk, + as strlen (src) <= len. */ + strcpy (dst, s1); + if (strncat (dst, "foo", 3) != dst || strcmp (dst, "hello worldfoo")) + abort (); + strcpy (dst, s1); + if (strncat (dst, "foo", 100) != dst || strcmp (dst, "hello worldfoo")) + abort (); + strcpy (dst, s1); + if (strncat (dst, s1, 100) != dst || strcmp (dst, "hello worldhello world")) + abort (); + if (chk_calls != 3) + abort (); + + chk_calls = 0; + /* The following calls have side-effects in dest, so are not checked. */ + strcpy (dst, s1); d2 = dst; + if (__builtin___strncat_chk (++d2, s1, 100, os (++d2)) != dst+1 + || d2 != dst+1 || strcmp (dst, "hello worldhello world")) + abort (); + strcpy (dst, s1); d2 = dst; + if (__builtin___strncat_chk (++d2+5, s1, 100, os (++d2+5)) != dst+6 + || d2 != dst+1 || strcmp (dst, "hello worldhello world")) + abort (); + strcpy (dst, s1); d2 = dst; + if (__builtin___strncat_chk (++d2+5, s1+5, 100, os (++d2+5)) != dst+6 + || d2 != dst+1 || strcmp (dst, "hello world world")) + abort (); + if (chk_calls) + abort (); + + chk_calls = 0; + strcat_disallowed = 1; + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + strcpy (dst, s1); + if (__builtin_strncat (dst, "", 100) != dst || strcmp (dst, s1)) + abort (); + + if (chk_calls) + abort (); + strncat_disallowed = 0; + strcat_disallowed = 0; +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + + /* The following calls should do runtime checking. */ + memset (&a, '\0', sizeof (a)); + s5 = (char *) &a; + __asm __volatile ("" : : "r" (s5) : "memory"); + chk_calls = 0; + strncat (a.buf1 + 2, s3 + 3, l1 - 1); + strncat (r, s3 + 2, l1); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memset (r, '\0', 3); + __asm __volatile ("" : : "r" (r) : "memory"); + strncat (r, s2 + 2, l1 + 1); + strncat (r + 2, s3 + 3, l1 - 1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + strncat (r, s2 + 4, l1); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known source length, + but we don't know the length of dest string, so runtime checking + is needed too. */ + memset (&a, '\0', sizeof (a)); + chk_calls = 0; + s5 = (char *) &a; + __asm __volatile ("" : : "r" (s5) : "memory"); + strncat (a.buf1 + 2, "a", 5); + strncat (r, "def", 0); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + memset (r, '\0', 3); + __asm __volatile ("" : : "r" (r) : "memory"); + strncat (r, s1 + 1, 2); + if (chk_calls != 2) + abort (); + chk_calls = 0; + strcat_disallowed = 1; + /* Unknown destination and source, no checking. */ + strncat (s4, s3, l1 + 1); + strcat_disallowed = 0; + if (chk_calls) + abort (); +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + memset (&a, '\0', sizeof (a)); + memset (buf3, '\0', sizeof (buf3)); + s5 = (char *) &a; + __asm __volatile ("" : : "r" (s5) : "memory"); + s5 = buf3; + __asm __volatile ("" : : "r" (s5) : "memory"); + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncat (&a.buf2[9], s2 + 3, 4); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncat (&a.buf2[7], s3 + strlen (s3) - 3, 3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncat (&buf3[19], "abcde", 1); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = p; + test1 (); + memset (p, '\0', sizeof (p)); + test2 (); + test3 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c new file mode 100644 index 00000000000..46f3374c4dd --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strncpy-chk.c @@ -0,0 +1,227 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __strncpy_chk performs correctly. */ + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strncpy (char *, const char *, size_t); +extern int memcmp (const void *, const void *, size_t); +extern int strcmp (const char *, const char *); +extern int strncmp (const char *, const char *, size_t); +extern void *memset (void *, int, size_t); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +int i; + +void +__attribute__((noinline)) +test1 (void) +{ + const char *const src = "hello world"; + const char *src2; + char dst[64], *dst2; + + strncpy_disallowed = 1; + chk_calls = 0; + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, src, 4) != dst || strncmp (dst, src, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst+16, src, 4) != dst+16 || strncmp (dst+16, src, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst+32, src+5, 4) != dst+32 || strncmp (dst+32, src+5, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + dst2 = dst; + if (strncpy (++dst2, src+5, 4) != dst+1 || strncmp (dst2, src+5, 4) + || dst2 != dst+1) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, src, 0) != dst || strcmp (dst, "")) + abort(); + + memset (dst, 0, sizeof (dst)); + dst2 = dst; src2 = src; + if (strncpy (++dst2, ++src2, 0) != dst+1 || strcmp (dst2, "") + || dst2 != dst+1 || src2 != src+1) + abort(); + + memset (dst, 0, sizeof (dst)); + dst2 = dst; src2 = src; + if (strncpy (++dst2+5, ++src2+5, 0) != dst+6 || strcmp (dst2+5, "") + || dst2 != dst+1 || src2 != src+1) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, src, 12) != dst || strcmp (dst, src)) + abort(); + + /* Test at least one instance of the __builtin_ style. We do this + to ensure that it works and that the prototype is correct. */ + memset (dst, 0, sizeof (dst)); + if (__builtin_strncpy (dst, src, 4) != dst || strncmp (dst, src, 4)) + abort(); + + memset (dst, 0, sizeof (dst)); + if (strncpy (dst, i++ ? "xfoo" + 1 : "bar", 4) != dst + || strcmp (dst, "bar") + || i != 1) + abort (); + + if (chk_calls) + abort (); + strncpy_disallowed = 0; +} + +void +__attribute__((noinline)) +test2 (void) +{ + chk_calls = 0; + /* No runtime checking should be done here, both destination + and length are unknown. */ + strncpy (s4, "abcd", l1 + 1); + if (chk_calls) + abort (); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test3 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int i; + const char *l; + size_t l2; + + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + chk_calls = 0; + strncpy (a.buf1 + 2, s3 + 3, l1); + strncpy (r, s3 + 2, l1 + 2); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + strncpy (r, s2 + 2, l1 + 2); + strncpy (r + 2, s3 + 3, l1); + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + strncpy (r, s2 + 4, l1); + if (chk_calls != 5) + abort (); + + /* Following have known destination and known length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + strncpy (a.buf1 + 2, "", 3); + strncpy (a.buf1 + 2, "", 0); + strncpy (r, "a", 1); + strncpy (r, "a", 3); + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + strncpy (r, s1 + 1, 3); + strncpy (r, s1 + 1, 2); + r = buf3; + l = "abc"; + l2 = 4; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1], l = "e", l2 = 2; + else if (i == l1) + r = &a.buf2[7], l = "gh", l2 = 3; + else if (i == l1 + 1) + r = &buf3[5], l = "jkl", l2 = 4; + else if (i == l1 + 2) + r = &a.buf1[9], l = "", l2 = 1; + } + strncpy (r, "", 1); + /* Here, strlen (l) + 1 is known to be at most 4 and + __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need + runtime checking. */ + strncpy (&buf3[16], l, l2); + strncpy (&buf3[15], "abc", l2); + strncpy (&buf3[10], "fghij", l2); + if (chk_calls) + abort (); + chk_calls = 0; +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test4 (void) +{ + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncpy (&a.buf2[9], s2 + 4, l1 + 1); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncpy (&a.buf2[7], s3, l1 + 4); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncpy (&buf3[19], "abc", 2); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + strncpy (&buf3[18], "", 3); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (); + s4 = p; + test2 (); + test3 (); + test4 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c new file mode 100644 index 00000000000..8c7d72f391d --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/vsnprintf-chk.c @@ -0,0 +1,321 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __vsnprintf_chk performs correctly. */ + +#include + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int memcmp (const void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern int vsnprintf (char *, size_t, const char *, va_list); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +static char buffer[32]; +char *ptr = "barf"; + +int +__attribute__((noinline)) +test1_sub (int i, ...) +{ + int ret = 0; + va_list ap; + va_start (ap, i); + switch (i) + { + case 0: + vsnprintf (buffer, 4, "foo", ap); + break; + case 1: + ret = vsnprintf (buffer, 4, "foo bar", ap); + break; + case 2: + vsnprintf (buffer, 32, "%s", ap); + break; + case 3: + ret = vsnprintf (buffer, 21, "%s", ap); + break; + case 4: + ret = vsnprintf (buffer, 4, "%d%d%d", ap); + break; + case 5: + ret = vsnprintf (buffer, 32, "%d%d%d", ap); + break; + case 6: + ret = vsnprintf (buffer, strlen (ptr) + 1, "%s", ap); + break; + case 7: + vsnprintf (buffer, l1 + 31, "%d - %c", ap); + break; + case 8: + vsnprintf (s4, l1 + 6, "%d - %c", ap); + break; + } + va_end (ap); + return ret; +} + +void +__attribute__((noinline)) +test1 (void) +{ + chk_calls = 0; + /* vsnprintf_disallowed = 1; */ + + memset (buffer, 'A', 32); + test1_sub (0); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (test1_sub (1) != 7) + abort (); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + vsnprintf_disallowed = 0; + + memset (buffer, 'A', 32); + test1_sub (2, "bar"); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (test1_sub (3, "bar") != 3) + abort (); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (test1_sub (4, (int) l1, (int) l1 + 1, (int) l1 + 12) != 4) + abort (); + if (memcmp (buffer, "121", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (test1_sub (5, (int) l1, (int) l1 + 1, (int) l1 + 12) != 4) + abort (); + if (memcmp (buffer, "1213", 5) || buffer[5] != 'A') + abort (); + + if (chk_calls) + abort (); + + memset (buffer, 'A', 32); + test1_sub (6, ptr); + if (memcmp (buffer, "barf", 5) || buffer[5] != 'A') + abort (); + + memset (buffer, 'A', 32); + test1_sub (7, (int) l1 + 27, *ptr); + if (memcmp (buffer, "28 - b\0AAAAA", 12)) + abort (); + + if (chk_calls != 2) + abort (); + chk_calls = 0; + + memset (s4, 'A', 32); + test1_sub (8, (int) l1 - 17, ptr[1]); + if (memcmp (s4, "-16 - \0AAA", 10)) + abort (); + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test2_sub (int i, ...) +{ + va_list ap; + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int j; + + va_start (ap, i); + /* The following calls should do runtime checking + - length is not known, but destination is. */ + switch (i) + { + case 0: + vsnprintf (a.buf1 + 2, l1, "%s", ap); + break; + case 1: + vsnprintf (r, l1 + 4, "%s%c", ap); + break; + case 2: + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vsnprintf (r, strlen (s2) - 2, "%c %s", ap); + break; + case 3: + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vsnprintf (r + 2, l1, s3 + 3, ap); + break; + case 4: + case 7: + r = buf3; + for (j = 0; j < 4; ++j) + { + if (j == l1 - 1) + r = &a.buf1[1]; + else if (j == l1) + r = &a.buf2[7]; + else if (j == l1 + 1) + r = &buf3[5]; + else if (j == l1 + 2) + r = &a.buf1[9]; + } + if (i == 4) + vsnprintf (r, l1, s2 + 4, ap); + else + vsnprintf (r, 1, "a", ap); + break; + case 5: + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vsnprintf (r, l1 + 3, "%s", ap); + break; + case 6: + vsnprintf (a.buf1 + 2, 4, "", ap); + break; + case 8: + vsnprintf (s4, 3, "%s %d", ap); + break; + } + va_end (ap); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + /* The following calls should do runtime checking + - length is not known, but destination is. */ + chk_calls = 0; + test2_sub (0, s3 + 3); + test2_sub (1, s3 + 3, s3[3]); + test2_sub (2, s2[2], s2 + 4); + test2_sub (3); + test2_sub (4); + test2_sub (5, s1 + 1); + if (chk_calls != 6) + abort (); + + /* Following have known destination and known source length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + /* vsnprintf_disallowed = 1; */ + test2_sub (6); + test2_sub (7); + vsnprintf_disallowed = 0; + /* Unknown destination and source, no checking. */ + test2_sub (8, s3, 0); + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test3_sub (int i, ...) +{ + va_list ap; + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + va_start (ap, i); + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + switch (i) + { + case 0: + vsnprintf (&a.buf2[9], l1 + 1, "%c%s", ap); + break; + case 1: + vsnprintf (&a.buf2[7], l1 + 30, "%s%c", ap); + break; + case 2: + vsnprintf (&a.buf2[7], l1 + 3, "%d", ap); + break; + case 3: + vsnprintf (&buf3[17], l1 + 3, "%s", ap); + break; + case 4: + vsnprintf (&buf3[19], 2, "a", ap); + break; + case 5: + vsnprintf (&buf3[16], 5, "a", ap); + break; + } + va_end (ap); +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (0, s2[3], s2 + 4); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (1, s3 + strlen (s3) - 2, *s3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (2, (int) l1 + 9999); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (3, "abc"); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (4); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (5); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = p; + test1 (); + test2 (); + test3 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c new file mode 100644 index 00000000000..9daf13e827b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk-lib.c @@ -0,0 +1 @@ +#include "lib/chk.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c new file mode 100644 index 00000000000..5c150901800 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/vsprintf-chk.c @@ -0,0 +1,290 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation. + + Ensure builtin __vsprintf_chk performs correctly. */ + +#include + +extern void abort (void); +typedef __SIZE_TYPE__ size_t; +extern size_t strlen(const char *); +extern void *memcpy (void *, const void *, size_t); +extern char *strcpy (char *, const char *); +extern int memcmp (const void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern int vsprintf (char *, const char *, va_list); + +#include "chk.h" + +const char s1[] = "123"; +char p[32] = ""; +char *s2 = "defg"; +char *s3 = "FGH"; +char *s4; +size_t l1 = 1; +static char buffer[32]; +char *ptr = "barf"; + +int +__attribute__((noinline)) +test1_sub (int i, ...) +{ + int ret = 0; + va_list ap; + va_start (ap, i); + switch (i) + { + case 0: + vsprintf (buffer, "foo", ap); + break; + case 1: + ret = vsprintf (buffer, "foo", ap); + break; + case 2: + vsprintf (buffer, "%s", ap); + break; + case 3: + ret = vsprintf (buffer, "%s", ap); + break; + case 4: + vsprintf (buffer, "%d - %c", ap); + break; + case 5: + vsprintf (s4, "%d - %c", ap); + break; + } + va_end (ap); + return ret; +} + +void +__attribute__((noinline)) +test1 (void) +{ + chk_calls = 0; + vsprintf_disallowed = 1; + + memset (buffer, 'A', 32); + test1_sub (0); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (test1_sub (1) != 3) + abort (); + if (memcmp (buffer, "foo", 4) || buffer[4] != 'A') + abort (); + + if (chk_calls) + abort (); + vsprintf_disallowed = 0; + + memset (buffer, 'A', 32); + test1_sub (2, "bar"); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + if (test1_sub (3, "bar") != 3) + abort (); + if (memcmp (buffer, "bar", 4) || buffer[4] != 'A') + abort (); + + memset (buffer, 'A', 32); + test1_sub (2, ptr); + if (memcmp (buffer, "barf", 5) || buffer[5] != 'A') + abort (); + + memset (buffer, 'A', 32); + test1_sub (4, (int) l1 + 27, *ptr); + if (memcmp (buffer, "28 - b\0AAAAA", 12)) + abort (); + + if (chk_calls != 4) + abort (); + chk_calls = 0; + + test1_sub (5, (int) l1 - 17, ptr[1]); + if (memcmp (s4, "-16 - a", 8)) + abort (); + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test2_sub (int i, ...) +{ + va_list ap; + struct A { char buf1[10]; char buf2[10]; } a; + char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4]; + char buf3[20]; + int j; + + va_start (ap, i); + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + switch (i) + { + case 0: + vsprintf (a.buf1 + 2, "%s", ap); + break; + case 1: + vsprintf (r, "%s%c", ap); + break; + case 2: + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vsprintf (r, "%c %s", ap); + break; + case 3: + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vsprintf (r + 2, s3 + 3, ap); + break; + case 4: + case 7: + r = buf3; + for (j = 0; j < 4; ++j) + { + if (j == l1 - 1) + r = &a.buf1[1]; + else if (j == l1) + r = &a.buf2[7]; + else if (j == l1 + 1) + r = &buf3[5]; + else if (j == l1 + 2) + r = &a.buf1[9]; + } + if (i == 4) + vsprintf (r, s2 + 4, ap); + else + vsprintf (r, "a", ap); + break; + case 5: + r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7]; + vsprintf (r, "%s", ap); + break; + case 6: + vsprintf (a.buf1 + 2, "", ap); + break; + case 8: + vsprintf (s4, "%s %d", ap); + break; + } + va_end (ap); +} + +/* Test whether compile time checking is done where it should + and so is runtime object size checking. */ +void +__attribute__((noinline)) +test2 (void) +{ + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + chk_calls = 0; + test2_sub (0, s3 + 3); + test2_sub (1, s3 + 3, s3[3]); + test2_sub (2, s2[2], s2 + 4); + test2_sub (3); + test2_sub (4); + test2_sub (5, s1 + 1); + if (chk_calls != 6) + abort (); + + /* Following have known destination and known source length, + so if optimizing certainly shouldn't result in the checking + variants. */ + chk_calls = 0; + vsprintf_disallowed = 1; + test2_sub (6); + test2_sub (7); + vsprintf_disallowed = 0; + /* Unknown destination and source, no checking. */ + test2_sub (8, s3, 0); + if (chk_calls) + abort (); +} + +void +__attribute__((noinline)) +test3_sub (int i, ...) +{ + va_list ap; + struct A { char buf1[10]; char buf2[10]; } a; + char buf3[20]; + + va_start (ap, i); + /* The following calls should do runtime checking + - source length is not known, but destination is. */ + switch (i) + { + case 0: + vsprintf (&a.buf2[9], "%c%s", ap); + break; + case 1: + vsprintf (&a.buf2[7], "%s%c", ap); + break; + case 2: + vsprintf (&a.buf2[7], "%d", ap); + break; + case 3: + vsprintf (&buf3[17], "%s", ap); + break; + case 4: + vsprintf (&buf3[19], "a", ap); + break; + } + va_end (ap); +} + +/* Test whether runtime and/or compile time checking catches + buffer overflows. */ +void +__attribute__((noinline)) +test3 (void) +{ + chk_fail_allowed = 1; + /* Runtime checks. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (0, s2[3], s2 + 4); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (1, s3 + strlen (s3) - 2, *s3); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (2, (int) l1 + 9999); + abort (); + } + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (3, "abc"); + abort (); + } + /* This should be detectable at compile time already. */ + if (__builtin_setjmp (chk_fail_buf) == 0) + { + test3_sub (4); + abort (); + } + chk_fail_allowed = 0; +} + +void +main_test (void) +{ +#ifndef __OPTIMIZE__ + /* Object size checking is only intended for -O[s123]. */ + return; +#endif + __asm ("" : "=r" (s2) : "0" (s2)); + __asm ("" : "=r" (s3) : "0" (s3)); + __asm ("" : "=r" (l1) : "0" (l1)); + s4 = p; + test1 (); + test2 (); + test3 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/fprintf-1.c b/gcc/testsuite/gcc.c-torture/execute/fprintf-1.c new file mode 100644 index 00000000000..f16252b1e8e --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/fprintf-1.c @@ -0,0 +1,23 @@ +#include +#include + +int +main (void) +{ +#define test(ret, args...) \ + fprintf (stdout, args); \ + if (fprintf (stdout, args) != ret) \ + abort (); + test (5, "hello"); + test (6, "hello\n"); + test (1, "a"); + test (0, ""); + test (5, "%s", "hello"); + test (6, "%s", "hello\n"); + test (1, "%s", "a"); + test (0, "%s", ""); + test (1, "%c", 'x'); + test (7, "%s\n", "hello\n"); + test (2, "%d\n", 0); + return 0; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/fprintf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/fprintf-chk-1.c new file mode 100644 index 00000000000..918ff8e5689 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/fprintf-chk-1.c @@ -0,0 +1,49 @@ +#include +#include +#include + +volatile int should_optimize; + +int +__attribute__((noinline)) +__fprintf_chk (FILE *f, int flag, const char *fmt, ...) +{ + va_list ap; + int ret; +#ifdef __OPTIMIZE__ + if (should_optimize) + abort (); +#endif + should_optimize = 1; + va_start (ap, fmt); + ret = vfprintf (f, fmt, ap); + va_end (ap); + return ret; +} + +int +main (void) +{ +#define test(ret, opt, args...) \ + should_optimize = opt; \ + __fprintf_chk (stdout, 1, args); \ + if (!should_optimize) \ + abort (); \ + should_optimize = 0; \ + if (__fprintf_chk (stdout, 1, args) != ret) \ + abort (); \ + if (!should_optimize) \ + abort (); + test (5, 1, "hello"); + test (6, 1, "hello\n"); + test (1, 1, "a"); + test (0, 1, ""); + test (5, 1, "%s", "hello"); + test (6, 1, "%s", "hello\n"); + test (1, 1, "%s", "a"); + test (0, 1, "%s", ""); + test (1, 1, "%c", 'x'); + test (7, 0, "%s\n", "hello\n"); + test (2, 0, "%d\n", 0); + return 0; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/printf-1.c b/gcc/testsuite/gcc.c-torture/execute/printf-1.c new file mode 100644 index 00000000000..0ffcd5d443b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/printf-1.c @@ -0,0 +1,23 @@ +#include +#include + +int +main (void) +{ +#define test(ret, args...) \ + printf (args); \ + if (printf (args) != ret) \ + abort (); + test (5, "hello"); + test (6, "hello\n"); + test (1, "a"); + test (0, ""); + test (5, "%s", "hello"); + test (6, "%s", "hello\n"); + test (1, "%s", "a"); + test (0, "%s", ""); + test (1, "%c", 'x'); + test (7, "%s\n", "hello\n"); + test (2, "%d\n", 0); + return 0; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/printf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/printf-chk-1.c new file mode 100644 index 00000000000..8f9a79c5c57 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/printf-chk-1.c @@ -0,0 +1,49 @@ +#include +#include +#include + +volatile int should_optimize; + +int +__attribute__((noinline)) +__printf_chk (int flag, const char *fmt, ...) +{ + va_list ap; + int ret; +#ifdef __OPTIMIZE__ + if (should_optimize) + abort (); +#endif + should_optimize = 1; + va_start (ap, fmt); + ret = vprintf (fmt, ap); + va_end (ap); + return ret; +} + +int +main (void) +{ +#define test(ret, opt, args...) \ + should_optimize = opt; \ + __printf_chk (1, args); \ + if (!should_optimize) \ + abort (); \ + should_optimize = 0; \ + if (__printf_chk (1, args) != ret) \ + abort (); \ + if (!should_optimize) \ + abort (); + test (5, 0, "hello"); + test (6, 1, "hello\n"); + test (1, 1, "a"); + test (0, 1, ""); + test (5, 0, "%s", "hello"); + test (6, 1, "%s", "hello\n"); + test (1, 1, "%s", "a"); + test (0, 1, "%s", ""); + test (1, 1, "%c", 'x'); + test (7, 1, "%s\n", "hello\n"); + test (2, 0, "%d\n", 0); + return 0; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/vfprintf-1.c b/gcc/testsuite/gcc.c-torture/execute/vfprintf-1.c new file mode 100644 index 00000000000..c0038042542 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/vfprintf-1.c @@ -0,0 +1,53 @@ +#ifndef test +#include +#include +#include + +void +inner (int x, ...) +{ + va_list ap, ap2; + va_start (ap, x); + va_start (ap2, x); + + switch (x) + { +#define test(n, ret, fmt, args) \ + case n: \ + vfprintf (stdout, fmt, ap); \ + if (vfprintf (stdout, fmt, ap2) != ret) \ + abort (); \ + break; +#include "vfprintf-1.c" +#undef test + default: + abort (); + } + + va_end (ap); + va_end (ap2); +} + +int +main (void) +{ +#define test(n, ret, fmt, args) \ + inner args; +#include "vfprintf-1.c" +#undef test + return 0; +} + +#else + test (0, 5, "hello", (0)); + test (1, 6, "hello\n", (1)); + test (2, 1, "a", (2)); + test (3, 0, "", (3)); + test (4, 5, "%s", (4, "hello")); + test (5, 6, "%s", (5, "hello\n")); + test (6, 1, "%s", (6, "a")); + test (7, 0, "%s", (7, "")); + test (8, 1, "%c", (8, 'x')); + test (9, 7, "%s\n", (9, "hello\n")); + test (10, 2, "%d\n", (10, 0)); +#endif diff --git a/gcc/testsuite/gcc.c-torture/execute/vfprintf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/vfprintf-chk-1.c new file mode 100644 index 00000000000..f8f964c7e9a --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/vfprintf-chk-1.c @@ -0,0 +1,73 @@ +#ifndef test +#include +#include +#include + +volatile int should_optimize; + +int +__attribute__((noinline)) +__vfprintf_chk (FILE *f, int flag, const char *fmt, va_list ap) +{ +#ifdef __OPTIMIZE__ + if (should_optimize) + abort (); +#endif + should_optimize = 1; + return vfprintf (f, fmt, ap); +} + +void +inner (int x, ...) +{ + va_list ap, ap2; + va_start (ap, x); + va_start (ap2, x); + + switch (x) + { +#define test(n, ret, opt, fmt, args) \ + case n: \ + should_optimize = opt; \ + __vfprintf_chk (stdout, 1, fmt, ap); \ + if (! should_optimize) \ + abort (); \ + should_optimize = 0; \ + if (__vfprintf_chk (stdout, 1, fmt, ap2) != ret) \ + abort (); \ + if (! should_optimize) \ + abort (); \ + break; +#include "vfprintf-chk-1.c" +#undef test + default: + abort (); + } + + va_end (ap); + va_end (ap2); +} + +int +main (void) +{ +#define test(n, ret, opt, fmt, args) \ + inner args; +#include "vfprintf-chk-1.c" +#undef test + return 0; +} + +#else + test (0, 5, 1, "hello", (0)); + test (1, 6, 1, "hello\n", (1)); + test (2, 1, 1, "a", (2)); + test (3, 0, 1, "", (3)); + test (4, 5, 0, "%s", (4, "hello")); + test (5, 6, 0, "%s", (5, "hello\n")); + test (6, 1, 0, "%s", (6, "a")); + test (7, 0, 0, "%s", (7, "")); + test (8, 1, 0, "%c", (8, 'x')); + test (9, 7, 0, "%s\n", (9, "hello\n")); + test (10, 2, 0, "%d\n", (10, 0)); +#endif diff --git a/gcc/testsuite/gcc.c-torture/execute/vprintf-1.c b/gcc/testsuite/gcc.c-torture/execute/vprintf-1.c new file mode 100644 index 00000000000..9f1b8bf67aa --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/vprintf-1.c @@ -0,0 +1,53 @@ +#ifndef test +#include +#include +#include + +void +inner (int x, ...) +{ + va_list ap, ap2; + va_start (ap, x); + va_start (ap2, x); + + switch (x) + { +#define test(n, ret, fmt, args) \ + case n: \ + vprintf (fmt, ap); \ + if (vprintf (fmt, ap2) != ret) \ + abort (); \ + break; +#include "vprintf-1.c" +#undef test + default: + abort (); + } + + va_end (ap); + va_end (ap2); +} + +int +main (void) +{ +#define test(n, ret, fmt, args) \ + inner args; +#include "vprintf-1.c" +#undef test + return 0; +} + +#else + test (0, 5, "hello", (0)); + test (1, 6, "hello\n", (1)); + test (2, 1, "a", (2)); + test (3, 0, "", (3)); + test (4, 5, "%s", (4, "hello")); + test (5, 6, "%s", (5, "hello\n")); + test (6, 1, "%s", (6, "a")); + test (7, 0, "%s", (7, "")); + test (8, 1, "%c", (8, 'x')); + test (9, 7, "%s\n", (9, "hello\n")); + test (10, 2, "%d\n", (10, 0)); +#endif diff --git a/gcc/testsuite/gcc.c-torture/execute/vprintf-chk-1.c b/gcc/testsuite/gcc.c-torture/execute/vprintf-chk-1.c new file mode 100644 index 00000000000..ca62f8b9fe2 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/vprintf-chk-1.c @@ -0,0 +1,73 @@ +#ifndef test +#include +#include +#include + +volatile int should_optimize; + +int +__attribute__((noinline)) +__vprintf_chk (int flag, const char *fmt, va_list ap) +{ +#ifdef __OPTIMIZE__ + if (should_optimize) + abort (); +#endif + should_optimize = 1; + return vprintf (fmt, ap); +} + +void +inner (int x, ...) +{ + va_list ap, ap2; + va_start (ap, x); + va_start (ap2, x); + + switch (x) + { +#define test(n, ret, opt, fmt, args) \ + case n: \ + should_optimize = opt; \ + __vprintf_chk (1, fmt, ap); \ + if (! should_optimize) \ + abort (); \ + should_optimize = 0; \ + if (__vprintf_chk (1, fmt, ap2) != ret) \ + abort (); \ + if (! should_optimize) \ + abort (); \ + break; +#include "vprintf-chk-1.c" +#undef test + default: + abort (); + } + + va_end (ap); + va_end (ap2); +} + +int +main (void) +{ +#define test(n, ret, opt, fmt, args) \ + inner args; +#include "vprintf-chk-1.c" +#undef test + return 0; +} + +#else + test (0, 5, 0, "hello", (0)); + test (1, 6, 1, "hello\n", (1)); + test (2, 1, 1, "a", (2)); + test (3, 0, 1, "", (3)); + test (4, 5, 0, "%s", (4, "hello")); + test (5, 6, 0, "%s", (5, "hello\n")); + test (6, 1, 0, "%s", (6, "a")); + test (7, 0, 0, "%s", (7, "")); + test (8, 1, 0, "%c", (8, 'x')); + test (9, 7, 0, "%s\n", (9, "hello\n")); + test (10, 2, 0, "%d\n", (10, 0)); +#endif diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c new file mode 100644 index 00000000000..404b7117f88 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c @@ -0,0 +1,436 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +extern void abort (void); +extern void exit (int); +extern void *malloc (size_t); +extern void *calloc (size_t, size_t); +extern void *alloca (size_t); +extern void *memcpy (void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern char *strcpy (char *, const char *); + +struct A +{ + char a[10]; + int b; + char c[10]; +} y, w[4]; + +extern char exta[]; +extern char extb[30]; +extern struct A zerol[0]; + +void +__attribute__ ((noinline)) +test1 (void *q, int x) +{ + struct A a; + void *p = &a.a[3], *r; + char var[x + 10]; + if (x < 0) + r = &a.a[9]; + else + r = &a.c[1]; + if (__builtin_object_size (p, 0) + != sizeof (a) - __builtin_offsetof (struct A, a) - 3) + abort (); + if (__builtin_object_size (&a.c[9], 0) + != sizeof (a) - __builtin_offsetof (struct A, c) - 9) + abort (); + if (__builtin_object_size (q, 0) != (size_t) -1) + abort (); + if (__builtin_object_size (r, 0) + != sizeof (a) - __builtin_offsetof (struct A, a) - 9) + abort (); + if (x < 6) + r = &w[2].a[1]; + else + r = &a.a[6]; + if (__builtin_object_size (&y, 0) + != sizeof (y)) + abort (); + if (__builtin_object_size (w, 0) + != sizeof (w)) + abort (); + if (__builtin_object_size (&y.b, 0) + != sizeof (a) - __builtin_offsetof (struct A, b)) + abort (); + if (__builtin_object_size (r, 0) + != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 16); + if (__builtin_object_size (r, 0) != 2 * 16) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 14); + if (__builtin_object_size (r, 0) != 30) + abort (); + if (x < 30) + r = malloc (sizeof (a)); + else + r = &a.a[3]; + if (__builtin_object_size (r, 0) != sizeof (a)) + abort (); + r = memcpy (r, "a", 2); + if (__builtin_object_size (r, 0) != sizeof (a)) + abort (); + r = memcpy (r + 2, "b", 2) + 2; + if (__builtin_object_size (r, 0) != sizeof (a) - 4) + abort (); + r = &a.a[4]; + r = memset (r, 'a', 2); + if (__builtin_object_size (r, 0) + != sizeof (a) - __builtin_offsetof (struct A, a) - 4) + abort (); + r = memset (r + 2, 'b', 2) + 2; + if (__builtin_object_size (r, 0) + != sizeof (a) - __builtin_offsetof (struct A, a) - 8) + abort (); + r = &a.a[1]; + r = strcpy (r, "ab"); + if (__builtin_object_size (r, 0) + != sizeof (a) - __builtin_offsetof (struct A, a) - 1) + abort (); + r = strcpy (r + 2, "cd") + 2; + if (__builtin_object_size (r, 0) + != sizeof (a) - __builtin_offsetof (struct A, a) - 5) + abort (); + if (__builtin_object_size (exta, 0) != (size_t) -1) + abort (); + if (__builtin_object_size (exta + 10, 0) != (size_t) -1) + abort (); + if (__builtin_object_size (&exta[5], 0) != (size_t) -1) + abort (); + if (__builtin_object_size (extb, 0) != sizeof (extb)) + abort (); + if (__builtin_object_size (extb + 10, 0) != sizeof (extb) - 10) + abort (); + if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5) + abort (); + if (__builtin_object_size (var, 0) != (size_t) -1) + abort (); + if (__builtin_object_size (var + 10, 0) != (size_t) -1) + abort (); + if (__builtin_object_size (&var[5], 0) != (size_t) -1) + abort (); + if (__builtin_object_size (zerol, 0) != 0) + abort (); + if (__builtin_object_size (&zerol, 0) != 0) + abort (); + if (__builtin_object_size (&zerol[0], 0) != 0) + abort (); + if (__builtin_object_size (zerol[0].a, 0) != 0) + abort (); + if (__builtin_object_size (&zerol[0].a[0], 0) != 0) + abort (); + if (__builtin_object_size (&zerol[0].b, 0) != 0) + abort (); + if (__builtin_object_size ("abcdefg", 0) != sizeof ("abcdefg")) + abort (); + if (__builtin_object_size ("abcd\0efg", 0) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg", 0) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[0], 0) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[4], 0) != sizeof ("abcd\0efg") - 4) + abort (); + if (__builtin_object_size ("abcd\0efg" + 5, 0) != sizeof ("abcd\0efg") - 5) + abort (); + if (__builtin_object_size (L"abcdefg", 0) != sizeof (L"abcdefg")) + abort (); + r = (char *) L"abcd\0efg"; + if (__builtin_object_size (r + 2, 0) != sizeof (L"abcd\0efg") - 2) + abort (); +} + +size_t l1 = 1; + +void +__attribute__ ((noinline)) +test2 (void) +{ + struct B { char buf1[10]; char buf2[10]; } a; + char *r, buf3[20]; + int i; + + if (sizeof (a) != 20) + return; + + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 0) != 20) + abort (); + r = &buf3[20]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[7]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 0) != 15) + abort (); + r += 8; + if (__builtin_object_size (r, 0) != 7) + abort (); + if (__builtin_object_size (r + 6, 0) != 1) + abort (); + r = &buf3[18]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[9]; + else if (i == l1) + r = &a.buf2[9]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[4]; + } + if (__builtin_object_size (r + 12, 0) != 4) + abort (); +} + +void +__attribute__ ((noinline)) +test3 (void) +{ + char buf4[10]; + struct B { struct A a[2]; struct A b; char c[4]; char d; double e; + _Complex double f; } x; + double y; + _Complex double z; + double *dp; + + if (__builtin_object_size (buf4, 0) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4, 0) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[0], 0) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[1], 0) != sizeof (buf4) - 1) + abort (); + if (__builtin_object_size (&x, 0) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a, 0) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0], 0) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0].a, 0) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0].a[0], 0) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0].a[3], 0) != sizeof (x) - 3) + abort (); + if (__builtin_object_size (&x.a[0].b, 0) + != sizeof (x) - __builtin_offsetof (struct A, b)) + abort (); + if (__builtin_object_size (&x.a[1].c, 0) + != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.a[1].c[0], 0) + != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.a[1].c[3], 0) + != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c) - 3) + abort (); + if (__builtin_object_size (&x.b, 0) + != sizeof (x) - __builtin_offsetof (struct B, b)) + abort (); + if (__builtin_object_size (&x.b.a, 0) + != sizeof (x) - __builtin_offsetof (struct B, b)) + abort (); + if (__builtin_object_size (&x.b.a[0], 0) + != sizeof (x) - __builtin_offsetof (struct B, b)) + abort (); + if (__builtin_object_size (&x.b.a[3], 0) + != sizeof (x) - __builtin_offsetof (struct B, b) - 3) + abort (); + if (__builtin_object_size (&x.b.b, 0) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, b)) + abort (); + if (__builtin_object_size (&x.b.c, 0) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.b.c[0], 0) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.b.c[3], 0) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, c) - 3) + abort (); + if (__builtin_object_size (&x.c, 0) + != sizeof (x) - __builtin_offsetof (struct B, c)) + abort (); + if (__builtin_object_size (&x.c[0], 0) + != sizeof (x) - __builtin_offsetof (struct B, c)) + abort (); + if (__builtin_object_size (&x.c[1], 0) + != sizeof (x) - __builtin_offsetof (struct B, c) - 1) + abort (); + if (__builtin_object_size (&x.d, 0) + != sizeof (x) - __builtin_offsetof (struct B, d)) + abort (); + if (__builtin_object_size (&x.e, 0) + != sizeof (x) - __builtin_offsetof (struct B, e)) + abort (); + if (__builtin_object_size (&x.f, 0) + != sizeof (x) - __builtin_offsetof (struct B, f)) + abort (); + dp = &__real__ x.f; + if (__builtin_object_size (dp, 0) + != sizeof (x) - __builtin_offsetof (struct B, f)) + abort (); + dp = &__imag__ x.f; + if (__builtin_object_size (dp, 0) + != sizeof (x) - __builtin_offsetof (struct B, f) + - sizeof (x.f) / 2) + abort (); + dp = &y; + if (__builtin_object_size (dp, 0) != sizeof (y)) + abort (); + if (__builtin_object_size (&z, 0) != sizeof (z)) + abort (); + dp = &__real__ z; + if (__builtin_object_size (dp, 0) != sizeof (z)) + abort (); + dp = &__imag__ z; + if (__builtin_object_size (dp, 0) != sizeof (z) / 2) + abort (); +} + +struct S { unsigned int a; }; + +char * +__attribute__ ((noinline)) +test4 (char *x, int y) +{ + register int i; + struct A *p; + + for (i = 0; i < y; i++) + { + p = (struct A *) x; + x = (char *) &p[1]; + if (__builtin_object_size (p, 0) != (size_t) -1) + abort (); + } + return x; +} + +void +__attribute__ ((noinline)) +test5 (size_t x) +{ + char buf[64]; + char *p = &buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + /* My understanding of ISO C99 6.5.6 is that a conforming + program will not end up with p equal to &buf[0] + through &buf[7], i.e. calling this function with say + UINTPTR_MAX / 4 results in undefined behaviour. + If that's true, then the maximum number of remaining + bytes from p until end of the object is 56, otherwise + it would be 64 (or conservative (size_t) -1 == unknown). */ + if (__builtin_object_size (p, 0) != sizeof (buf) - 8) + abort (); + memset (p, ' ', sizeof (buf) - 8 - 4 * 4); +} + +void +__attribute__ ((noinline)) +test6 (size_t x) +{ + struct T { char buf[64]; char buf2[64]; } t; + char *p = &t.buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 0) != sizeof (t) - 8) + abort (); + memset (p, ' ', sizeof (t) - 8 - 4 * 4); +} + +void +__attribute__ ((noinline)) +test7 (void) +{ + char buf[64]; + struct T { char buf[64]; char buf2[64]; } t; + char *p = &buf[64], *q = &t.buf[64]; + + if (__builtin_object_size (p + 64, 0) != 0) + abort (); + if (__builtin_object_size (q + 63, 0) != sizeof (t) - 64 - 63) + abort (); + if (__builtin_object_size (q + 64, 0) != sizeof (t) - 64 - 64) + abort (); + if (__builtin_object_size (q + 256, 0) != 0) + abort (); +} + +void +__attribute__ ((noinline)) +test8 (void) +{ + struct T { char buf[10]; char buf2[10]; } t; + char *p = &t.buf2[-4]; + char *q = &t.buf2[0]; + if (__builtin_object_size (p, 0) != sizeof (t) - 10 + 4) + abort (); + if (__builtin_object_size (q, 0) != sizeof (t) - 10) + abort (); + /* GCC only handles additions, not subtractions. */ + q = q - 8; + if (__builtin_object_size (q, 0) != (size_t) -1 + && __builtin_object_size (q, 0) != sizeof (t) - 10 + 8) + abort (); + p = &t.buf[-4]; + if (__builtin_object_size (p, 0) != 0) + abort (); +} + +int +main (void) +{ + struct S s[10]; + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (main, 6); + test2 (); + test3 (); + test4 ((char *) s, 10); + test5 (4); + test6 (4); + test7 (); + test8 (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c new file mode 100644 index 00000000000..4071c2516ee --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c @@ -0,0 +1,393 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +extern void abort (void); +extern void exit (int); +extern void *malloc (size_t); +extern void *calloc (size_t, size_t); +extern void *alloca (size_t); +extern void *memcpy (void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern char *strcpy (char *, const char *); + +struct A +{ + char a[10]; + int b; + char c[10]; +} y, w[4]; + +extern char exta[]; +extern char extb[30]; +extern struct A extc[]; +struct A zerol[0]; + +void +__attribute__ ((noinline)) +test1 (void *q, int x) +{ + struct A a; + void *p = &a.a[3], *r; + char var[x + 10]; + struct A vara[x + 10]; + if (x < 0) + r = &a.a[9]; + else + r = &a.c[1]; + if (__builtin_object_size (p, 1) != sizeof (a.a) - 3) + abort (); + if (__builtin_object_size (&a.c[9], 1) + != sizeof (a.c) - 9) + abort (); + if (__builtin_object_size (q, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (r, 1) != sizeof (a.c) - 1) + abort (); + if (x < 6) + r = &w[2].a[1]; + else + r = &a.a[6]; + if (__builtin_object_size (&y, 1) != sizeof (y)) + abort (); + if (__builtin_object_size (w, 1) != sizeof (w)) + abort (); + if (__builtin_object_size (&y.b, 1) != sizeof (a.b)) + abort (); + if (__builtin_object_size (r, 1) != sizeof (a.a) - 1) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 16); + if (__builtin_object_size (r, 1) != 2 * 16) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 14); + if (__builtin_object_size (r, 1) != 30) + abort (); + if (x < 30) + r = malloc (sizeof (a)); + else + r = &a.a[3]; + if (__builtin_object_size (r, 1) != sizeof (a)) + abort (); + r = memcpy (r, "a", 2); + if (__builtin_object_size (r, 1) != sizeof (a)) + abort (); + r = memcpy (r + 2, "b", 2) + 2; + if (__builtin_object_size (r, 1) != sizeof (a) - 4) + abort (); + r = &a.a[4]; + r = memset (r, 'a', 2); + if (__builtin_object_size (r, 1) != sizeof (a.a) - 4) + abort (); + r = memset (r + 2, 'b', 2) + 2; + if (__builtin_object_size (r, 1) != sizeof (a.a) - 8) + abort (); + r = &a.a[1]; + r = strcpy (r, "ab"); + if (__builtin_object_size (r, 1) != sizeof (a.a) - 1) + abort (); + r = strcpy (r + 2, "cd") + 2; + if (__builtin_object_size (r, 1) != sizeof (a.a) - 5) + abort (); + if (__builtin_object_size (exta, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (exta + 10, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&exta[5], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (extb, 1) != sizeof (extb)) + abort (); + if (__builtin_object_size (extb + 10, 1) != sizeof (extb) - 10) + abort (); + if (__builtin_object_size (&extb[5], 1) != sizeof (extb) - 5) + abort (); + if (__builtin_object_size (extc, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (extc + 10, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&extc[5], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&extc->a, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&(extc + 10)->b, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (var, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (var + 10, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&var[5], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (vara, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (vara + 10, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&vara[5], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&vara[0].a, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&vara[10].a[0], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&vara[5].a[4], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&vara[5].b, 1) != (size_t) -1) + abort (); + if (__builtin_object_size (&vara[7].c[7], 1) != (size_t) -1) + abort (); + if (__builtin_object_size (zerol, 1) != 0) + abort (); + if (__builtin_object_size (&zerol, 1) != 0) + abort (); + if (__builtin_object_size (&zerol[0], 1) != 0) + abort (); + if (__builtin_object_size (zerol[0].a, 1) != 0) + abort (); + if (__builtin_object_size (&zerol[0].a[0], 1) != 0) + abort (); + if (__builtin_object_size (&zerol[0].b, 1) != 0) + abort (); + if (__builtin_object_size ("abcdefg", 1) != sizeof ("abcdefg")) + abort (); + if (__builtin_object_size ("abcd\0efg", 1) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg", 1) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[0], 1) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[4], 1) != sizeof ("abcd\0efg") - 4) + abort (); + if (__builtin_object_size ("abcd\0efg" + 5, 1) != sizeof ("abcd\0efg") - 5) + abort (); + if (__builtin_object_size (L"abcdefg", 1) != sizeof (L"abcdefg")) + abort (); + r = (char *) L"abcd\0efg"; + if (__builtin_object_size (r + 2, 1) != sizeof (L"abcd\0efg") - 2) + abort (); +} + +size_t l1 = 1; + +void +__attribute__ ((noinline)) +test2 (void) +{ + struct B { char buf1[10]; char buf2[10]; } a; + char *r, buf3[20]; + int i; + + if (sizeof (a) != 20) + return; + + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 1) != sizeof (buf3)) + abort (); + r = &buf3[20]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[7]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 1) != sizeof (buf3) - 5) + abort (); + r += 8; + if (__builtin_object_size (r, 1) != sizeof (buf3) - 13) + abort (); + if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19) + abort (); +} + +void +__attribute__ ((noinline)) +test3 (void) +{ + char buf4[10]; + struct B { struct A a[2]; struct A b; char c[4]; char d; double e; + _Complex double f; } x; + double y; + _Complex double z; + double *dp; + + if (__builtin_object_size (buf4, 1) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4, 1) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[0], 1) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[1], 1) != sizeof (buf4) - 1) + abort (); + if (__builtin_object_size (&x, 1) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a, 1) != sizeof (x.a)) + abort (); + if (__builtin_object_size (&x.a[0], 1) != sizeof (x.a)) + abort (); + if (__builtin_object_size (&x.a[0].a, 1) != sizeof (x.a[0].a)) + abort (); + if (__builtin_object_size (&x.a[0].a[0], 1) != sizeof (x.a[0].a)) + abort (); + if (__builtin_object_size (&x.a[0].a[3], 1) != sizeof (x.a[0].a) - 3) + abort (); + if (__builtin_object_size (&x.a[0].b, 1) != sizeof (x.a[0].b)) + abort (); + if (__builtin_object_size (&x.a[1].c, 1) != sizeof (x.a[1].c)) + abort (); + if (__builtin_object_size (&x.a[1].c[0], 1) != sizeof (x.a[1].c)) + abort (); + if (__builtin_object_size (&x.a[1].c[3], 1) != sizeof (x.a[1].c) - 3) + abort (); + if (__builtin_object_size (&x.b, 1) != sizeof (x.b)) + abort (); + if (__builtin_object_size (&x.b.a, 1) != sizeof (x.b.a)) + abort (); + if (__builtin_object_size (&x.b.a[0], 1) != sizeof (x.b.a)) + abort (); + if (__builtin_object_size (&x.b.a[3], 1) != sizeof (x.b.a) - 3) + abort (); + if (__builtin_object_size (&x.b.b, 1) != sizeof (x.b.b)) + abort (); + if (__builtin_object_size (&x.b.c, 1) != sizeof (x.b.c)) + abort (); + if (__builtin_object_size (&x.b.c[0], 1) != sizeof (x.b.c)) + abort (); + if (__builtin_object_size (&x.b.c[3], 1) != sizeof (x.b.c) - 3) + abort (); + if (__builtin_object_size (&x.c, 1) != sizeof (x.c)) + abort (); + if (__builtin_object_size (&x.c[0], 1) != sizeof (x.c)) + abort (); + if (__builtin_object_size (&x.c[1], 1) != sizeof (x.c) - 1) + abort (); + if (__builtin_object_size (&x.d, 1) != sizeof (x.d)) + abort (); + if (__builtin_object_size (&x.e, 1) != sizeof (x.e)) + abort (); + if (__builtin_object_size (&x.f, 1) != sizeof (x.f)) + abort (); + dp = &__real__ x.f; + if (__builtin_object_size (dp, 1) != sizeof (x.f) / 2) + abort (); + dp = &__imag__ x.f; + if (__builtin_object_size (dp, 1) != sizeof (x.f) / 2) + abort (); + dp = &y; + if (__builtin_object_size (dp, 1) != sizeof (y)) + abort (); + if (__builtin_object_size (&z, 1) != sizeof (z)) + abort (); + dp = &__real__ z; + if (__builtin_object_size (dp, 1) != sizeof (z) / 2) + abort (); + dp = &__imag__ z; + if (__builtin_object_size (dp, 1) != sizeof (z) / 2) + abort (); +} + +struct S { unsigned int a; }; + +char * +__attribute__ ((noinline)) +test4 (char *x, int y) +{ + register int i; + struct A *p; + + for (i = 0; i < y; i++) + { + p = (struct A *) x; + x = (char *) &p[1]; + if (__builtin_object_size (p, 1) != (size_t) -1) + abort (); + } + return x; +} + +void +__attribute__ ((noinline)) +test5 (size_t x) +{ + struct T { char buf[64]; char buf2[64]; } t; + char *p = &t.buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8) + abort (); + memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4); +} + +void +__attribute__ ((noinline)) +test6 (void) +{ + char buf[64]; + struct T { char buf[64]; char buf2[64]; } t; + char *p = &buf[64], *q = &t.buf[64]; + + if (__builtin_object_size (p + 64, 1) != 0) + abort (); + if (__builtin_object_size (q + 0, 1) != 0) + abort (); + if (__builtin_object_size (q + 64, 1) != 0) + abort (); +} + +void +__attribute__ ((noinline)) +test7 (void) +{ + struct T { char buf[10]; char buf2[10]; } t; + char *p = &t.buf2[-4]; + char *q = &t.buf2[0]; + if (__builtin_object_size (p, 1) != 0) + abort (); + if (__builtin_object_size (q, 1) != sizeof (t.buf2)) + abort (); + q = &t.buf[10]; + if (__builtin_object_size (q, 1) != 0) + abort (); + q = &t.buf[11]; + if (__builtin_object_size (q, 1) != 0) + abort (); + p = &t.buf[-4]; + if (__builtin_object_size (p, 1) != 0) + abort (); +} + +int +main (void) +{ + struct S s[10]; + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (main, 6); + test2 (); + test3 (); + test4 ((char *) s, 10); + test5 (4); + test6 (); + test7 (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c new file mode 100644 index 00000000000..572ecda0436 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c @@ -0,0 +1,446 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +extern void abort (void); +extern void exit (int); +extern void *malloc (size_t); +extern void *calloc (size_t, size_t); +extern void *alloca (size_t); +extern void *memcpy (void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern char *strcpy (char *, const char *); + +struct A +{ + char a[10]; + int b; + char c[10]; +} y, w[4]; + +extern char exta[]; +extern char extb[30]; +extern struct A zerol[0]; + +void +__attribute__ ((noinline)) +test1 (void *q, int x) +{ + struct A a; + void *p = &a.a[3], *r; + char var[x + 10]; + if (x < 0) + r = &a.a[9]; + else + r = &a.c[1]; + if (__builtin_object_size (p, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 3) + abort (); + if (__builtin_object_size (&a.c[9], 2) + != sizeof (a) - __builtin_offsetof (struct A, c) - 9) + abort (); + if (__builtin_object_size (q, 2) != 0) + abort (); + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, c) - 1) + abort (); + if (x < 6) + r = &w[2].a[1]; + else + r = &a.a[6]; + if (__builtin_object_size (&y, 2) + != sizeof (y)) + abort (); + if (__builtin_object_size (w, 2) + != sizeof (w)) + abort (); + if (__builtin_object_size (&y.b, 2) + != sizeof (a) - __builtin_offsetof (struct A, b)) + abort (); + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 6) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 16); + if (__builtin_object_size (r, 2) != 30) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 14); + if (__builtin_object_size (r, 2) != 2 * 14) + abort (); + if (x < 30) + r = malloc (sizeof (a)); + else + r = &a.a[3]; + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 3) + abort (); + r = memcpy (r, "a", 2); + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 3) + abort (); + r = memcpy (r + 2, "b", 2) + 2; + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4) + abort (); + r = &a.a[4]; + r = memset (r, 'a', 2); + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 4) + abort (); + r = memset (r + 2, 'b', 2) + 2; + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 8) + abort (); + r = &a.a[1]; + r = strcpy (r, "ab"); + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 1) + abort (); + r = strcpy (r + 2, "cd") + 2; + if (__builtin_object_size (r, 2) + != sizeof (a) - __builtin_offsetof (struct A, a) - 5) + abort (); + if (__builtin_object_size (exta, 2) != 0) + abort (); + if (__builtin_object_size (exta + 10, 2) != 0) + abort (); + if (__builtin_object_size (&exta[5], 2) != 0) + abort (); + if (__builtin_object_size (extb, 2) != sizeof (extb)) + abort (); + if (__builtin_object_size (extb + 10, 2) != sizeof (extb) - 10) + abort (); + if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5) + abort (); + if (__builtin_object_size (var, 2) != 0) + abort (); + if (__builtin_object_size (var + 10, 2) != 0) + abort (); + if (__builtin_object_size (&var[5], 2) != 0) + abort (); + if (__builtin_object_size (zerol, 2) != 0) + abort (); + if (__builtin_object_size (&zerol, 2) != 0) + abort (); + if (__builtin_object_size (&zerol[0], 2) != 0) + abort (); + if (__builtin_object_size (zerol[0].a, 2) != 0) + abort (); + if (__builtin_object_size (&zerol[0].a[0], 2) != 0) + abort (); + if (__builtin_object_size (&zerol[0].b, 2) != 0) + abort (); + if (__builtin_object_size ("abcdefg", 2) != sizeof ("abcdefg")) + abort (); + if (__builtin_object_size ("abcd\0efg", 2) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg", 2) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[0], 2) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[4], 2) != sizeof ("abcd\0efg") - 4) + abort (); + if (__builtin_object_size ("abcd\0efg" + 5, 2) != sizeof ("abcd\0efg") - 5) + abort (); + if (__builtin_object_size (L"abcdefg", 2) != sizeof (L"abcdefg")) + abort (); + r = (char *) L"abcd\0efg"; + if (__builtin_object_size (r + 2, 2) != sizeof (L"abcd\0efg") - 2) + abort (); +} + +size_t l1 = 1; + +void +__attribute__ ((noinline)) +test2 (void) +{ + struct B { char buf1[10]; char buf2[10]; } a; + char *r, buf3[20]; + int i; + + if (sizeof (a) != 20) + return; + + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 2) != 3) + abort (); + r = &buf3[20]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[7]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 2) != 0) + abort (); + r = &buf3[2]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf1[2]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[4]; + } + if (__builtin_object_size (r, 2) != 15) + abort (); + r += 8; + if (__builtin_object_size (r, 2) != 7) + abort (); + if (__builtin_object_size (r + 6, 2) != 1) + abort (); + r = &buf3[18]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[9]; + else if (i == l1) + r = &a.buf2[9]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[4]; + } + if (__builtin_object_size (r + 12, 2) != 0) + abort (); +} + +void +__attribute__ ((noinline)) +test3 (void) +{ + char buf4[10]; + struct B { struct A a[2]; struct A b; char c[4]; char d; double e; + _Complex double f; } x; + double y; + _Complex double z; + double *dp; + + if (__builtin_object_size (buf4, 2) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4, 2) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[0], 2) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[1], 2) != sizeof (buf4) - 1) + abort (); + if (__builtin_object_size (&x, 2) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a, 2) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0], 2) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0].a, 2) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0].a[0], 2) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a[0].a[3], 2) != sizeof (x) - 3) + abort (); + if (__builtin_object_size (&x.a[0].b, 2) + != sizeof (x) - __builtin_offsetof (struct A, b)) + abort (); + if (__builtin_object_size (&x.a[1].c, 2) + != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.a[1].c[0], 2) + != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.a[1].c[3], 2) + != sizeof (x) - sizeof (struct A) - __builtin_offsetof (struct A, c) - 3) + abort (); + if (__builtin_object_size (&x.b, 2) + != sizeof (x) - __builtin_offsetof (struct B, b)) + abort (); + if (__builtin_object_size (&x.b.a, 2) + != sizeof (x) - __builtin_offsetof (struct B, b)) + abort (); + if (__builtin_object_size (&x.b.a[0], 2) + != sizeof (x) - __builtin_offsetof (struct B, b)) + abort (); + if (__builtin_object_size (&x.b.a[3], 2) + != sizeof (x) - __builtin_offsetof (struct B, b) - 3) + abort (); + if (__builtin_object_size (&x.b.b, 2) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, b)) + abort (); + if (__builtin_object_size (&x.b.c, 2) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.b.c[0], 2) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, c)) + abort (); + if (__builtin_object_size (&x.b.c[3], 2) + != sizeof (x) - __builtin_offsetof (struct B, b) + - __builtin_offsetof (struct A, c) - 3) + abort (); + if (__builtin_object_size (&x.c, 2) + != sizeof (x) - __builtin_offsetof (struct B, c)) + abort (); + if (__builtin_object_size (&x.c[0], 2) + != sizeof (x) - __builtin_offsetof (struct B, c)) + abort (); + if (__builtin_object_size (&x.c[1], 2) + != sizeof (x) - __builtin_offsetof (struct B, c) - 1) + abort (); + if (__builtin_object_size (&x.d, 2) + != sizeof (x) - __builtin_offsetof (struct B, d)) + abort (); + if (__builtin_object_size (&x.e, 2) + != sizeof (x) - __builtin_offsetof (struct B, e)) + abort (); + if (__builtin_object_size (&x.f, 2) + != sizeof (x) - __builtin_offsetof (struct B, f)) + abort (); + dp = &__real__ x.f; + if (__builtin_object_size (dp, 2) + != sizeof (x) - __builtin_offsetof (struct B, f)) + abort (); + dp = &__imag__ x.f; + if (__builtin_object_size (dp, 2) + != sizeof (x) - __builtin_offsetof (struct B, f) + - sizeof (x.f) / 2) + abort (); + dp = &y; + if (__builtin_object_size (dp, 2) != sizeof (y)) + abort (); + if (__builtin_object_size (&z, 2) != sizeof (z)) + abort (); + dp = &__real__ z; + if (__builtin_object_size (dp, 2) != sizeof (z)) + abort (); + dp = &__imag__ z; + if (__builtin_object_size (dp, 2) != sizeof (z) / 2) + abort (); +} + +struct S { unsigned int a; }; + +char * +__attribute__ ((noinline)) +test4 (char *x, int y) +{ + register int i; + struct A *p; + + for (i = 0; i < y; i++) + { + p = (struct A *) x; + x = (char *) &p[1]; + if (__builtin_object_size (p, 2) != 0) + abort (); + } + return x; +} + +void +__attribute__ ((noinline)) +test5 (size_t x) +{ + char buf[64]; + char *p = &buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 2) != 0) + abort (); + memset (p, ' ', sizeof (buf) - 8 - 4 * 4); +} + +void +__attribute__ ((noinline)) +test6 (size_t x) +{ + struct T { char buf[64]; char buf2[64]; } t; + char *p = &t.buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 2) != 0) + abort (); + memset (p, ' ', sizeof (t) - 8 - 4 * 4); +} + +void +__attribute__ ((noinline)) +test7 (void) +{ + char buf[64]; + struct T { char buf[64]; char buf2[64]; } t; + char *p = &buf[64], *q = &t.buf[64]; + + if (__builtin_object_size (p + 64, 2) != 0) + abort (); + if (__builtin_object_size (q + 63, 2) != sizeof (t) - 64 - 63) + abort (); + if (__builtin_object_size (q + 64, 2) != sizeof (t) - 64 - 64) + abort (); + if (__builtin_object_size (q + 256, 2) != 0) + abort (); +} + +void +__attribute__ ((noinline)) +test8 (void) +{ + struct T { char buf[10]; char buf2[10]; } t; + char *p = &t.buf2[-4]; + char *q = &t.buf2[0]; + if (__builtin_object_size (p, 2) != sizeof (t) - 10 + 4) + abort (); + if (__builtin_object_size (q, 2) != sizeof (t) - 10) + abort (); + /* GCC only handles additions, not subtractions. */ + q = q - 8; + if (__builtin_object_size (q, 2) != 0 + && __builtin_object_size (q, 2) != sizeof (t) - 10 + 8) + abort (); + p = &t.buf[-4]; + if (__builtin_object_size (p, 2) != 0) + abort (); +} + +int +main (void) +{ + struct S s[10]; + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (main, 6); + test2 (); + test3 (); + test4 ((char *) s, 10); + test5 (4); + test6 (4); + test7 (); + test8 (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c new file mode 100644 index 00000000000..453c2d01921 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c @@ -0,0 +1,407 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +extern void abort (void); +extern void exit (int); +extern void *malloc (size_t); +extern void *calloc (size_t, size_t); +extern void *alloca (size_t); +extern void *memcpy (void *, const void *, size_t); +extern void *memset (void *, int, size_t); +extern char *strcpy (char *, const char *); + +struct A +{ + char a[10]; + int b; + char c[10]; +} y, w[4]; + +extern char exta[]; +extern char extb[30]; +extern struct A extc[]; +struct A zerol[0]; + +void +__attribute__ ((noinline)) +test1 (void *q, int x) +{ + struct A a; + void *p = &a.a[3], *r; + char var[x + 10]; + struct A vara[x + 10]; + if (x < 0) + r = &a.a[9]; + else + r = &a.c[1]; + if (__builtin_object_size (p, 3) != sizeof (a.a) - 3) + abort (); + if (__builtin_object_size (&a.c[9], 3) + != sizeof (a.c) - 9) + abort (); + if (__builtin_object_size (q, 3) != 0) + abort (); + if (__builtin_object_size (r, 3) != sizeof (a.a) - 9) + abort (); + if (x < 6) + r = &w[2].a[1]; + else + r = &a.a[6]; + if (__builtin_object_size (&y, 3) != sizeof (y)) + abort (); + if (__builtin_object_size (w, 3) != sizeof (w)) + abort (); + if (__builtin_object_size (&y.b, 3) != sizeof (a.b)) + abort (); + if (__builtin_object_size (r, 3) != sizeof (a.a) - 6) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 16); + if (__builtin_object_size (r, 3) != 30) + abort (); + if (x < 20) + r = malloc (30); + else + r = calloc (2, 14); + if (__builtin_object_size (r, 3) != 2 * 14) + abort (); + if (x < 30) + r = malloc (sizeof (a)); + else + r = &a.a[3]; + if (__builtin_object_size (r, 3) != sizeof (a.a) - 3) + abort (); + r = memcpy (r, "a", 2); + if (__builtin_object_size (r, 3) != sizeof (a.a) - 3) + abort (); + r = memcpy (r + 2, "b", 2) + 2; + if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4) + abort (); + r = &a.a[4]; + r = memset (r, 'a', 2); + if (__builtin_object_size (r, 3) != sizeof (a.a) - 4) + abort (); + r = memset (r + 2, 'b', 2) + 2; + if (__builtin_object_size (r, 3) != sizeof (a.a) - 8) + abort (); + r = &a.a[1]; + r = strcpy (r, "ab"); + if (__builtin_object_size (r, 3) != sizeof (a.a) - 1) + abort (); + r = strcpy (r + 2, "cd") + 2; + if (__builtin_object_size (r, 3) != sizeof (a.a) - 5) + abort (); + if (__builtin_object_size (exta, 3) != 0) + abort (); + if (__builtin_object_size (exta + 10, 3) != 0) + abort (); + if (__builtin_object_size (&exta[5], 3) != 0) + abort (); + if (__builtin_object_size (extb, 3) != sizeof (extb)) + abort (); + if (__builtin_object_size (extb + 10, 3) != sizeof (extb) - 10) + abort (); + if (__builtin_object_size (&extb[5], 3) != sizeof (extb) - 5) + abort (); + if (__builtin_object_size (extc, 3) != 0) + abort (); + if (__builtin_object_size (extc + 10, 3) != 0) + abort (); + if (__builtin_object_size (&extc[5], 3) != 0) + abort (); + if (__builtin_object_size (&extc->a, 3) != 0) + abort (); + if (__builtin_object_size (&(extc + 10)->b, 3) != 0) + abort (); + if (__builtin_object_size (&extc[5].c[3], 3) != 0) + abort (); + if (__builtin_object_size (var, 3) != 0) + abort (); + if (__builtin_object_size (var + 10, 3) != 0) + abort (); + if (__builtin_object_size (&var[5], 3) != 0) + abort (); + if (__builtin_object_size (vara, 3) != 0) + abort (); + if (__builtin_object_size (vara + 10, 3) != 0) + abort (); + if (__builtin_object_size (&vara[5], 3) != 0) + abort (); + if (__builtin_object_size (&vara[0].a, 3) != 0) + abort (); + if (__builtin_object_size (&vara[10].a[0], 3) != 0) + abort (); + if (__builtin_object_size (&vara[5].a[4], 3) != 0) + abort (); + if (__builtin_object_size (&vara[5].b, 3) != 0) + abort (); + if (__builtin_object_size (&vara[7].c[7], 3) != 0) + abort (); + if (__builtin_object_size (zerol, 3) != 0) + abort (); + if (__builtin_object_size (&zerol, 3) != 0) + abort (); + if (__builtin_object_size (&zerol[0], 3) != 0) + abort (); + if (__builtin_object_size (zerol[0].a, 3) != 0) + abort (); + if (__builtin_object_size (&zerol[0].a[0], 3) != 0) + abort (); + if (__builtin_object_size (&zerol[0].b, 3) != 0) + abort (); + if (__builtin_object_size ("abcdefg", 3) != sizeof ("abcdefg")) + abort (); + if (__builtin_object_size ("abcd\0efg", 3) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg", 3) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[0], 3) != sizeof ("abcd\0efg")) + abort (); + if (__builtin_object_size (&"abcd\0efg"[4], 3) != sizeof ("abcd\0efg") - 4) + abort (); + if (__builtin_object_size ("abcd\0efg" + 5, 3) != sizeof ("abcd\0efg") - 5) + abort (); + if (__builtin_object_size (L"abcdefg", 3) != sizeof (L"abcdefg")) + abort (); + r = (char *) L"abcd\0efg"; + if (__builtin_object_size (r + 2, 3) != sizeof (L"abcd\0efg") - 2) + abort (); +} + +size_t l1 = 1; + +void +__attribute__ ((noinline)) +test2 (void) +{ + struct B { char buf1[10]; char buf2[10]; } a; + char *r, buf3[20]; + int i; + + if (sizeof (a) != 20) + return; + + r = buf3; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[1]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 9) + abort (); + r = &buf3[20]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[7]; + else if (i == l1) + r = &a.buf2[7]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[9]; + } + if (__builtin_object_size (r, 3) != 0) + abort (); + r = &buf3[1]; + for (i = 0; i < 4; ++i) + { + if (i == l1 - 1) + r = &a.buf1[6]; + else if (i == l1) + r = &a.buf2[4]; + else if (i == l1 + 1) + r = &buf3[5]; + else if (i == l1 + 2) + r = &a.buf1[2]; + } + if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6) + abort (); + r += 2; + if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2) + abort (); + if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3) + abort (); +} + +void +__attribute__ ((noinline)) +test3 (void) +{ + char buf4[10]; + struct B { struct A a[2]; struct A b; char c[4]; char d; double e; + _Complex double f; } x; + double y; + _Complex double z; + double *dp; + + if (__builtin_object_size (buf4, 3) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4, 3) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[0], 3) != sizeof (buf4)) + abort (); + if (__builtin_object_size (&buf4[1], 3) != sizeof (buf4) - 1) + abort (); + if (__builtin_object_size (&x, 3) != sizeof (x)) + abort (); + if (__builtin_object_size (&x.a, 3) != sizeof (x.a)) + abort (); + if (__builtin_object_size (&x.a[0], 3) != sizeof (x.a)) + abort (); + if (__builtin_object_size (&x.a[0].a, 3) != sizeof (x.a[0].a)) + abort (); + if (__builtin_object_size (&x.a[0].a[0], 3) != sizeof (x.a[0].a)) + abort (); + if (__builtin_object_size (&x.a[0].a[3], 3) != sizeof (x.a[0].a) - 3) + abort (); + if (__builtin_object_size (&x.a[0].b, 3) != sizeof (x.a[0].b)) + abort (); + if (__builtin_object_size (&x.a[1].c, 3) != sizeof (x.a[1].c)) + abort (); + if (__builtin_object_size (&x.a[1].c[0], 3) != sizeof (x.a[1].c)) + abort (); + if (__builtin_object_size (&x.a[1].c[3], 3) != sizeof (x.a[1].c) - 3) + abort (); + if (__builtin_object_size (&x.b, 3) != sizeof (x.b)) + abort (); + if (__builtin_object_size (&x.b.a, 3) != sizeof (x.b.a)) + abort (); + if (__builtin_object_size (&x.b.a[0], 3) != sizeof (x.b.a)) + abort (); + if (__builtin_object_size (&x.b.a[3], 3) != sizeof (x.b.a) - 3) + abort (); + if (__builtin_object_size (&x.b.b, 3) != sizeof (x.b.b)) + abort (); + if (__builtin_object_size (&x.b.c, 3) != sizeof (x.b.c)) + abort (); + if (__builtin_object_size (&x.b.c[0], 3) != sizeof (x.b.c)) + abort (); + if (__builtin_object_size (&x.b.c[3], 3) != sizeof (x.b.c) - 3) + abort (); + if (__builtin_object_size (&x.c, 3) != sizeof (x.c)) + abort (); + if (__builtin_object_size (&x.c[0], 3) != sizeof (x.c)) + abort (); + if (__builtin_object_size (&x.c[1], 3) != sizeof (x.c) - 1) + abort (); + if (__builtin_object_size (&x.d, 3) != sizeof (x.d)) + abort (); + if (__builtin_object_size (&x.e, 3) != sizeof (x.e)) + abort (); + if (__builtin_object_size (&x.f, 3) != sizeof (x.f)) + abort (); + dp = &__real__ x.f; + if (__builtin_object_size (dp, 3) != sizeof (x.f) / 2) + abort (); + dp = &__imag__ x.f; + if (__builtin_object_size (dp, 3) != sizeof (x.f) / 2) + abort (); + dp = &y; + if (__builtin_object_size (dp, 3) != sizeof (y)) + abort (); + if (__builtin_object_size (&z, 3) != sizeof (z)) + abort (); + dp = &__real__ z; + if (__builtin_object_size (dp, 3) != sizeof (z) / 2) + abort (); + dp = &__imag__ z; + if (__builtin_object_size (dp, 3) != sizeof (z) / 2) + abort (); +} + +struct S { unsigned int a; }; + +char * +__attribute__ ((noinline)) +test4 (char *x, int y) +{ + register int i; + struct A *p; + + for (i = 0; i < y; i++) + { + p = (struct A *) x; + x = (char *) &p[1]; + if (__builtin_object_size (p, 3) != 0) + abort (); + } + return x; +} + +void +__attribute__ ((noinline)) +test5 (size_t x) +{ + struct T { char buf[64]; char buf2[64]; } t; + char *p = &t.buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 3) != 0) + abort (); + memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4); +} + +void +__attribute__ ((noinline)) +test6 (void) +{ + char buf[64]; + struct T { char buf[64]; char buf2[64]; } t; + char *p = &buf[64], *q = &t.buf[64]; + + if (__builtin_object_size (p + 64, 3) != 0) + abort (); + if (__builtin_object_size (q + 0, 3) != 0) + abort (); + if (__builtin_object_size (q + 64, 3) != 0) + abort (); +} + +void +__attribute__ ((noinline)) +test7 (void) +{ + struct T { char buf[10]; char buf2[10]; } t; + char *p = &t.buf2[-4]; + char *q = &t.buf2[0]; + if (__builtin_object_size (p, 3) != 0) + abort (); + if (__builtin_object_size (q, 3) != sizeof (t.buf2)) + abort (); + q = &t.buf[10]; + if (__builtin_object_size (q, 3) != 0) + abort (); + q = &t.buf[11]; + if (__builtin_object_size (q, 3) != 0) + abort (); + p = &t.buf[-4]; + if (__builtin_object_size (p, 3) != 0) + abort (); +} + +int +main (void) +{ + struct S s[10]; + __asm ("" : "=r" (l1) : "0" (l1)); + test1 (main, 6); + test2 (); + test3 (); + test4 ((char *) s, 10); + test5 (4); + test6 (); + test7 (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c new file mode 100644 index 00000000000..4cb580ebc9a --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c @@ -0,0 +1,56 @@ +/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; +extern void abort (void); +extern char buf[0x40000000]; + +void +test1 (size_t x) +{ + char *p = &buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 0) != sizeof (buf) - 8) + abort (); +} + +void +test2 (size_t x) +{ + char *p = &buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 1) != sizeof (buf) - 8) + abort (); +} + +void +test3 (size_t x) +{ + char *p = &buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 2) != 0) + abort (); +} + +void +test4 (size_t x) +{ + char *p = &buf[8]; + size_t i; + + for (i = 0; i < x; ++i) + p = p + 4; + if (__builtin_object_size (p, 3) != 0) + abort (); +} + +/* { dg-final { scan-assembler-not "abort" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c new file mode 100644 index 00000000000..aaf32884e50 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c @@ -0,0 +1,113 @@ +/* Test whether buffer overflow warnings for __*_chk builtins + are emitted properly. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -std=gnu99" } */ + +extern void abort (void); + +#include "../gcc.c-torture/execute/builtins/chk.h" +#include + +volatile void *vx; +char buf1[20]; +int x; + +void +test (int arg, ...) +{ + char buf2[20]; + va_list ap; + char *p = &buf1[10], *q; + + memcpy (&buf2[19], "ab", 1); + memcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "memcpy" } */ + vx = mempcpy (&buf2[19], "ab", 1); + vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "mempcpy" } */ + memmove (&buf2[18], &buf1[10], 2); + memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "will always overflow" "memmove" } */ + memset (&buf2[16], 'a', 4); + memset (&buf2[15], 'b', 6); /* { dg-warning "will always overflow" "memset" } */ + strcpy (&buf2[18], "a"); + strcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcpy" } */ + vx = stpcpy (&buf2[18], "a"); + vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "stpcpy" } */ + strncpy (&buf2[18], "a", 2); + strncpy (&buf2[18], "a", 3); /* { dg-warning "will always overflow" "strncpy" } */ + strncpy (&buf2[18], "abc", 2); + strncpy (&buf2[18], "abc", 3); /* { dg-warning "will always overflow" "strncpy" } */ + memset (buf2, '\0', sizeof (buf2)); + strcat (&buf2[18], "a"); + memset (buf2, '\0', sizeof (buf2)); + strcat (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcat" } */ + sprintf (&buf2[18], "%s", buf1); + sprintf (&buf2[18], "%s", "a"); + sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "will always overflow" "sprintf" } */ + sprintf (&buf2[18], "a"); + sprintf (&buf2[18], "ab"); /* { dg-warning "will always overflow" "sprintf" } */ + snprintf (&buf2[18], 2, "%d", x); + /* N argument to snprintf is the size of the buffer. + Although this particular call wouldn't overflow buf2, + incorrect buffer size was passed to it and therefore + we want a warning and runtime failure. */ + snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "will always overflow" "snprintf" } */ + va_start (ap, arg); + vsprintf (&buf2[18], "a", ap); + va_end (ap); + va_start (ap, arg); + vsprintf (&buf2[18], "ab", ap); /* { dg-warning "will always overflow" "vsprintf" } */ + va_end (ap); + va_start (ap, arg); + vsnprintf (&buf2[18], 2, "%s", ap); + va_end (ap); + va_start (ap, arg); + /* See snprintf above. */ + vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "will always overflow" "vsnprintf" } */ + va_end (ap); + + p = p + 10; + memset (p, 'd', 0); + q = strcpy (p, ""); /* { dg-warning "will always overflow" "strcpy" } */ + + /* This invokes undefined behaviour, since we are past the end of buf1. */ + p = p + 10; + memset (p, 'd', 1); /* { dg-warning "will always overflow" "memset" } */ + + memset (q, 'd', 0); + memset (q, 'd', 1); /* { dg-warning "will always overflow" "memset" } */ + q = q - 10; + memset (q, 'd', 10); +} + +char *str = "ABCDEFG"; +typedef struct { char b[16]; } H; + +/* Some brown paper bag bugs found in real applications. + This test is here merely for amusement. */ + +void +test2 (const H h) +{ + char c; + strncpy (&c, str, 3); /* { dg-warning "will always overflow" "strncpy" } */ + + struct { char b[4]; } x; + sprintf (x.b, "%s", "ABCD"); /* { dg-warning "will always overflow" "sprintf" } */ + + unsigned int i; + memcpy (&i, &h, sizeof (h)); /* { dg-warning "will always overflow" "memcpy" } */ + + unsigned char buf[21]; + memset (buf + 16, 0, 8); /* { dg-warning "will always overflow" "memset" } */ + + typedef struct { int i, j, k, l; } S; + S *s[3]; + memset (s, 0, sizeof (S) * 3); /* { dg-warning "will always overflow" "memset" } */ + + struct T { char a[8]; char b[4]; char c[10]; } t; + stpcpy (t.c,"Testing..."); /* { dg-warning "will always overflow" "stpcpy" } */ + + char b1[7]; + char b2[4]; + memset (b1, 0, sizeof (b1)); + memset (b2, 0, sizeof (b1)); /* { dg-warning "will always overflow" "memset" } */ +} diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c new file mode 100644 index 00000000000..adccd0f444d --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c @@ -0,0 +1,137 @@ +/* This file was miscompiled by an earlier version of the object size + checking patch. Object size in one of the memcpy calls was + incorrectly determined to be 0 while it should be (size_t) -1 + (== unknown). */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include "../gcc.c-torture/execute/builtins/chk.h" + +void *bar (int); +extern void *malloc (__SIZE_TYPE__); + +struct A +{ + int i, j, k; +}; + +/* Here all object sizes are not known at compile time. There + should be no warning, nor any checker functions called. */ + +void +foo (const struct A *x, int y, const unsigned char *z) +{ + unsigned int b; + unsigned char *c = 0; + + b = (x->i & 0xff) == 1 ? 3 : 4; + if (y) + c = bar (x->j * x->k); + + const unsigned char *d = z; + unsigned char *e = c; + unsigned char *f = c + x->j * x->k; + int g = 0; + + while (e < f) + { + unsigned int h = *d++; + + if (h & 128) + { + h = h - 128; + g = e + h * b > f; + if (g) + h = (f - e) / b; + if (b < 4) + do + { + memcpy (e, d, 3); + e += 3; + } + while (--h); + else + do + { + memcpy (e, d, 4); + e += 4; + } + while (--h); + d += b; + } + else + { + h *= b; + g = e + h > f; + if (g) + h = f - e; + memcpy (e, d, h); + e += h; + d += h; + } + } +} + +/* The same routine, slightly modified: + 1) c has known size at compile time + 2) e += h was changed into e += 16. + GCC could actually through VRP determine that + in e += h is (h >= 0 && h <= 127), thus know + it is pointer addition and not subtraction and + know e's __builtin_object_size (e, 0) is at 512, + but we are not there yet. */ + +unsigned char * +baz (const struct A *x, const unsigned char *z) +{ + unsigned int b; + unsigned char *c = 0; + + b = (x->i & 0xff) == 1 ? 3 : 4; + c = malloc (512); + + const unsigned char *d = z; + unsigned char *e = c; + unsigned char *f = c + x->j * x->k; + int g = 0; + + while (e < f) + { + unsigned int h = *d++; + + if (h & 128) + { + h = h - 128; + g = e + h * b > f; + if (g) + h = (f - e) / b; + if (b < 4) + do + { + memcpy (e, d, 3); + e += 3; + } + while (--h); + else + do + { + memcpy (e, d, 513); /* { dg-warning "will always overflow" "memcpy" } */ + e += 4; + } + while (--h); + d += b; + } + else + { + h *= b; + g = e + h > f; + if (g) + h = f - e; + memcpy (e, d, h); + /* e += h; */ + e += 16; + d += h; + } + } + return c; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c new file mode 100644 index 00000000000..b00c0a656b1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +typedef struct { int i; } FILE; +FILE *fp; +extern int fprintf (FILE *, const char *, ...); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9; + +void test (void) +{ + vi0 = 0; + fprintf (fp, "hello"); + vi1 = 0; + fprintf (fp, "hello\n"); + vi2 = 0; + fprintf (fp, "a"); + vi3 = 0; + fprintf (fp, ""); + vi4 = 0; + fprintf (fp, "%s", "hello"); + vi5 = 0; + fprintf (fp, "%s", "hello\n"); + vi6 = 0; + fprintf (fp, "%s", "a"); + vi7 = 0; + fprintf (fp, "%c", 'x'); + vi8 = 0; + fprintf (fp, "%d%d", vi0, vi1); + vi9 = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*fputc.*fp.*vi7" "fab"} } */ +/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "fab"} } */ +/* { dg-final { scan-tree-dump "vi8.*fprintf.*fp.*\"%d%d\".*vi9" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c new file mode 100644 index 00000000000..210bedbb415 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +typedef struct { int i; } FILE; +FILE *fp; +extern int __fprintf_chk (FILE *, int, const char *, ...); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9; + +void test (void) +{ + vi0 = 0; + __fprintf_chk (fp, 1, "hello"); + vi1 = 0; + __fprintf_chk (fp, 1, "hello\n"); + vi2 = 0; + __fprintf_chk (fp, 1, "a"); + vi3 = 0; + __fprintf_chk (fp, 1, ""); + vi4 = 0; + __fprintf_chk (fp, 1, "%s", "hello"); + vi5 = 0; + __fprintf_chk (fp, 1, "%s", "hello\n"); + vi6 = 0; + __fprintf_chk (fp, 1, "%s", "a"); + vi7 = 0; + __fprintf_chk (fp, 1, "%c", 'x'); + vi8 = 0; + __fprintf_chk (fp, 1, "%d%d", vi0, vi1); + vi9 = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*fputc.*fp.*vi7" "fab"} } */ +/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "fab"} } */ +/* { dg-final { scan-tree-dump "vi8.*__fprintf_chk.*fp.*1.*\"%d%d\".*vi9" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c new file mode 100644 index 00000000000..a98413446bd --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +extern int printf (const char *, ...); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via; + +void test (void) +{ + vi0 = 0; + printf ("hello"); + vi1 = 0; + printf ("hello\n"); + vi2 = 0; + printf ("a"); + vi3 = 0; + printf (""); + vi4 = 0; + printf ("%s", "hello"); + vi5 = 0; + printf ("%s", "hello\n"); + vi6 = 0; + printf ("%s", "a"); + vi7 = 0; + printf ("%s", ""); + vi8 = 0; + printf ("%c", 'x'); + vi9 = 0; + printf ("%s\n", "hello\n"); + via = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*printf.*\"hello\".*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*printf.*\"hello\".*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "fab"} } */ +/* { dg-final { scan-tree-dump "vi7 = 0\[^\(\)\]*vi8 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab"} } */ +/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c new file mode 100644 index 00000000000..532a3f4dead --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +extern int __printf_chk (int, const char *, ...); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via; + +void test (void) +{ + vi0 = 0; + __printf_chk (1, "hello"); + vi1 = 0; + __printf_chk (1, "hello\n"); + vi2 = 0; + __printf_chk (1, "a"); + vi3 = 0; + __printf_chk (1, ""); + vi4 = 0; + __printf_chk (1, "%s", "hello"); + vi5 = 0; + __printf_chk (1, "%s", "hello\n"); + vi6 = 0; + __printf_chk (1, "%s", "a"); + vi7 = 0; + __printf_chk (1, "%s", ""); + vi8 = 0; + __printf_chk (1, "%c", 'x'); + vi9 = 0; + __printf_chk (1, "%s\n", "hello\n"); + via = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*__printf_chk.*1.*\"hello\".*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*__printf_chk.*1.*\"hello\".*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "fab"} } */ +/* { dg-final { scan-tree-dump "vi7 = 0\[^\(\)\]*vi8 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab"} } */ +/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c new file mode 100644 index 00000000000..d82b311600d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +#include + +typedef struct { int i; } FILE; +FILE *fp; +extern int vfprintf (FILE *, const char *, va_list); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via; + +void +test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5, + va_list ap6, va_list ap7) +{ + vi0 = 0; + vfprintf (fp, "hello", ap1); + vi1 = 0; + vfprintf (fp, "hello\n", ap2); + vi2 = 0; + vfprintf (fp, "a", ap3); + vi3 = 0; + vfprintf (fp, "", ap4); + vi4 = 0; + vfprintf (fp, "%s", ap5); + vi5 = 0; + vfprintf (fp, "%c", ap6); + vi6 = 0; + vfprintf (fp, "%s\n", ap7); + vi7 = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*vfprintf.*\"%s\".*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*vfprintf.*\"%c\".*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*vfprintf.*\"%s\\\\n\".*vi7" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c new file mode 100644 index 00000000000..60a51aa2888 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +#include + +typedef struct { int i; } FILE; +FILE *fp; +extern int __vfprintf_chk (FILE *, int, const char *, va_list); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via; + +void +test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5, + va_list ap6, va_list ap7) +{ + vi0 = 0; + __vfprintf_chk (fp, 1, "hello", ap1); + vi1 = 0; + __vfprintf_chk (fp, 1, "hello\n", ap2); + vi2 = 0; + __vfprintf_chk (fp, 1, "a", ap3); + vi3 = 0; + __vfprintf_chk (fp, 1, "", ap4); + vi4 = 0; + __vfprintf_chk (fp, 1, "%s", ap5); + vi5 = 0; + __vfprintf_chk (fp, 1, "%c", ap6); + vi6 = 0; + __vfprintf_chk (fp, 1, "%s\n", ap7); + vi7 = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*__vfprintf_chk.*fp.*1.*\"%s\".*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*__vfprintf_chk.*fp.*1.*\"%c\".*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*__vfprintf_chk.*fp.*1.*\"%s\\\\n\".*vi7" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c new file mode 100644 index 00000000000..9518105915d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +#include + +extern int vprintf (const char *, va_list); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via; + +void +test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5, + va_list ap6, va_list ap7) +{ + vi0 = 0; + vprintf ("hello", ap1); + vi1 = 0; + vprintf ("hello\n", ap2); + vi2 = 0; + vprintf ("a", ap3); + vi3 = 0; + vprintf ("", ap4); + vi4 = 0; + vprintf ("%s", ap5); + vi5 = 0; + vprintf ("%c", ap6); + vi6 = 0; + vprintf ("%s\n", ap7); + vi7 = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*vprintf.*\"hello\".*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*vprintf.*\"%s\".*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*vprintf.*\"%c\".*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*vprintf.*\"%s\\\\n\".*vi7" "fab"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c new file mode 100644 index 00000000000..d95e336730a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-fab" } */ + +#include + +extern int __vprintf_chk (int, const char *, va_list); +volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via; + +void +test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5, + va_list ap6, va_list ap7) +{ + vi0 = 0; + __vprintf_chk (1, "hello", ap1); + vi1 = 0; + __vprintf_chk (1, "hello\n", ap2); + vi2 = 0; + __vprintf_chk (1, "a", ap3); + vi3 = 0; + __vprintf_chk (1, "", ap4); + vi4 = 0; + __vprintf_chk (1, "%s", ap5); + vi5 = 0; + __vprintf_chk (1, "%c", ap6); + vi6 = 0; + __vprintf_chk (1, "%s\n", ap7); + vi7 = 0; +} + +/* { dg-final { scan-tree-dump "vi0.*__vprintf_chk.*1.*\"hello\".*vi1" "fab"} } */ +/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab"} } */ +/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab"} } */ +/* { dg-final { scan-tree-dump "vi3 = 0\[^\(\)\]*vi4 = 0" "fab"} } */ +/* { dg-final { scan-tree-dump "vi4.*__vprintf_chk.*1.*\"%s\".*vi5" "fab"} } */ +/* { dg-final { scan-tree-dump "vi5.*__vprintf_chk.*1.*\"%c\".*vi6" "fab"} } */ +/* { dg-final { scan-tree-dump "vi6.*__vprintf_chk.*1.*\"%s\\\\n\".*vi7" "fab"} } */ diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c new file mode 100644 index 00000000000..2ba11c0b09c --- /dev/null +++ b/gcc/tree-object-size.c @@ -0,0 +1,1078 @@ +/* __builtin_object_size (ptr, object_size_type) computation + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Jakub Jelinek + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "diagnostic.h" +#include "tree-flow.h" +#include "tree-pass.h" +#include "tree-ssa-propagate.h" + +struct object_size_info +{ + int object_size_type; + bitmap visited, reexamine; + int pass; + bool changed; + unsigned int *depths; + unsigned int *stack, *tos; +}; + +static unsigned HOST_WIDE_INT unknown[4] = { -1, -1, 0, 0 }; + +static tree compute_object_offset (tree, tree); +static unsigned HOST_WIDE_INT addr_object_size (tree, int); +static unsigned HOST_WIDE_INT alloc_object_size (tree, int); +static tree pass_through_call (tree); +static void collect_object_sizes_for (struct object_size_info *, tree); +static void expr_object_size (struct object_size_info *, tree, tree); +static bool merge_object_sizes (struct object_size_info *, tree, tree, + unsigned HOST_WIDE_INT); +static bool plus_expr_object_size (struct object_size_info *, tree, tree); +static void compute_object_sizes (void); +static void init_offset_limit (void); +static void check_for_plus_in_loops (struct object_size_info *, tree); +static void check_for_plus_in_loops_1 (struct object_size_info *, tree, + unsigned int); + +/* object_sizes[0] is upper bound for number of bytes till the end of + the object. + object_sizes[1] is upper bound for number of bytes till the end of + the subobject (innermost array or field with address taken). + object_sizes[2] is lower bound for number of bytes till the end of + the object and object_sizes[3] lower bound for subobject. */ +static unsigned HOST_WIDE_INT *object_sizes[4]; + +/* Bitmaps what object sizes have been computed already. */ +static bitmap computed[4]; + +/* Maximum value of offset we consider to be addition. */ +static unsigned HOST_WIDE_INT offset_limit; + + +/* Initialize OFFSET_LIMIT variable. */ +static void +init_offset_limit (void) +{ + if (host_integerp (TYPE_MAX_VALUE (sizetype), 1)) + offset_limit = tree_low_cst (TYPE_MAX_VALUE (sizetype), 1); + else + offset_limit = -1; + offset_limit /= 2; +} + + +/* Compute offset of EXPR within VAR. Return error_mark_node + if unknown. */ + +static tree +compute_object_offset (tree expr, tree var) +{ + enum tree_code code = PLUS_EXPR; + tree base, off, t; + + if (expr == var) + return size_zero_node; + + switch (TREE_CODE (expr)) + { + case COMPONENT_REF: + base = compute_object_offset (TREE_OPERAND (expr, 0), var); + if (base == error_mark_node) + return base; + + t = TREE_OPERAND (expr, 1); + off = size_binop (PLUS_EXPR, DECL_FIELD_OFFSET (t), + size_int (tree_low_cst (DECL_FIELD_BIT_OFFSET (t), 1) + / BITS_PER_UNIT)); + break; + + case REALPART_EXPR: + case NOP_EXPR: + case CONVERT_EXPR: + case VIEW_CONVERT_EXPR: + case NON_LVALUE_EXPR: + return compute_object_offset (TREE_OPERAND (expr, 0), var); + + case IMAGPART_EXPR: + base = compute_object_offset (TREE_OPERAND (expr, 0), var); + if (base == error_mark_node) + return base; + + off = TYPE_SIZE_UNIT (TREE_TYPE (expr)); + break; + + case ARRAY_REF: + base = compute_object_offset (TREE_OPERAND (expr, 0), var); + if (base == error_mark_node) + return base; + + t = TREE_OPERAND (expr, 1); + if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0) + { + code = MINUS_EXPR; + t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t); + } + t = convert (sizetype, t); + off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t); + break; + + default: + return error_mark_node; + } + + return size_binop (code, base, off); +} + + +/* Compute __builtin_object_size for PTR, which is a ADDR_EXPR. + OBJECT_SIZE_TYPE is the second argument from __builtin_object_size. + If unknown, return unknown[object_size_type]. */ + +static unsigned HOST_WIDE_INT +addr_object_size (tree ptr, int object_size_type) +{ + tree pt_var; + + gcc_assert (TREE_CODE (ptr) == ADDR_EXPR); + + pt_var = TREE_OPERAND (ptr, 0); + if (REFERENCE_CLASS_P (pt_var)) + pt_var = get_base_address (pt_var); + + if (pt_var + && (SSA_VAR_P (pt_var) || TREE_CODE (pt_var) == STRING_CST) + && TYPE_SIZE_UNIT (TREE_TYPE (pt_var)) + && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1) + && (unsigned HOST_WIDE_INT) + tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1) < offset_limit) + { + tree bytes; + + if (pt_var != TREE_OPERAND (ptr, 0)) + { + tree var; + + if (object_size_type & 1) + { + var = TREE_OPERAND (ptr, 0); + + while (var != pt_var + && TREE_CODE (var) != BIT_FIELD_REF + && TREE_CODE (var) != COMPONENT_REF + && TREE_CODE (var) != ARRAY_REF + && TREE_CODE (var) != ARRAY_RANGE_REF + && TREE_CODE (var) != REALPART_EXPR + && TREE_CODE (var) != IMAGPART_EXPR) + var = TREE_OPERAND (var, 0); + if (var != pt_var && TREE_CODE (var) == ARRAY_REF) + var = TREE_OPERAND (var, 0); + if (! TYPE_SIZE_UNIT (TREE_TYPE (var)) + || ! host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (var)), 1) + || tree_int_cst_lt (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), + TYPE_SIZE_UNIT (TREE_TYPE (var)))) + var = pt_var; + } + else + var = pt_var; + + bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var); + if (bytes != error_mark_node) + { + if (TREE_CODE (bytes) == INTEGER_CST + && tree_int_cst_lt (TYPE_SIZE_UNIT (TREE_TYPE (var)), bytes)) + bytes = size_zero_node; + else + bytes = size_binop (MINUS_EXPR, + TYPE_SIZE_UNIT (TREE_TYPE (var)), bytes); + } + } + else + bytes = TYPE_SIZE_UNIT (TREE_TYPE (pt_var)); + + if (host_integerp (bytes, 1)) + return tree_low_cst (bytes, 1); + } + + return unknown[object_size_type]; +} + + +/* Compute __builtin_object_size for CALL, which is a CALL_EXPR. + Handles various allocation calls. OBJECT_SIZE_TYPE is the second + argument from __builtin_object_size. If unknown, return + unknown[object_size_type]. */ + +static unsigned HOST_WIDE_INT +alloc_object_size (tree call, int object_size_type) +{ + tree callee, arglist, a, bytes = NULL_TREE; + unsigned int arg_mask = 0; + + gcc_assert (TREE_CODE (call) == CALL_EXPR); + + callee = get_callee_fndecl (call); + arglist = TREE_OPERAND (call, 1); + if (callee + && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (callee)) + { + case BUILT_IN_MALLOC: + case BUILT_IN_ALLOCA: + arg_mask = 1; + break; + /* + case BUILT_IN_REALLOC: + arg_mask = 2; + break; + */ + case BUILT_IN_CALLOC: + arg_mask = 3; + break; + default: + break; + } + + for (a = arglist; arg_mask && a; arg_mask >>= 1, a = TREE_CHAIN (a)) + if (arg_mask & 1) + { + tree arg = TREE_VALUE (a); + + if (TREE_CODE (arg) != INTEGER_CST) + break; + + if (! bytes) + bytes = fold_convert (sizetype, arg); + else + bytes = size_binop (MULT_EXPR, bytes, + fold_convert (sizetype, arg)); + } + + if (! arg_mask && bytes && host_integerp (bytes, 1)) + return tree_low_cst (bytes, 1); + + return unknown[object_size_type]; +} + + +/* If object size is propagated from one of function's arguments directly + to its return value, return that argument for CALL_EXPR CALL. + Otherwise return NULL. */ + +static tree +pass_through_call (tree call) +{ + tree callee = get_callee_fndecl (call); + tree arglist = TREE_OPERAND (call, 1); + + if (callee + && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (callee)) + { + case BUILT_IN_MEMCPY: + case BUILT_IN_MEMMOVE: + case BUILT_IN_MEMSET: + case BUILT_IN_STRCPY: + case BUILT_IN_STRNCPY: + case BUILT_IN_STRCAT: + case BUILT_IN_STRNCAT: + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_STRNCAT_CHK: + if (arglist) + return TREE_VALUE (arglist); + break; + default: + break; + } + + return NULL_TREE; +} + + +/* Compute __builtin_object_size value for PTR. OBJECT_SIZE_TYPE is the + second argument from __builtin_object_size. */ + +unsigned HOST_WIDE_INT +compute_builtin_object_size (tree ptr, int object_size_type) +{ + gcc_assert (object_size_type >= 0 && object_size_type <= 3); + + if (! offset_limit) + init_offset_limit (); + + if (TREE_CODE (ptr) == ADDR_EXPR) + return addr_object_size (ptr, object_size_type); + else if (TREE_CODE (ptr) == CALL_EXPR) + { + tree arg = pass_through_call (ptr); + + if (arg) + return compute_builtin_object_size (arg, object_size_type); + else + return alloc_object_size (ptr, object_size_type); + } + else if (TREE_CODE (ptr) == SSA_NAME + && POINTER_TYPE_P (TREE_TYPE (ptr)) + && object_sizes[object_size_type] != NULL) + { + if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr))) + { + struct object_size_info osi; + bitmap_iterator bi; + unsigned int i; + + if (dump_file) + { + fprintf (dump_file, "Computing %s %sobject size for ", + (object_size_type & 2) ? "minimum" : "maximum", + (object_size_type & 1) ? "sub" : ""); + print_generic_expr (dump_file, ptr, dump_flags); + fprintf (dump_file, ":\n"); + } + + osi.visited = BITMAP_ALLOC (NULL); + osi.reexamine = BITMAP_ALLOC (NULL); + osi.object_size_type = object_size_type; + osi.depths = NULL; + osi.stack = NULL; + osi.tos = NULL; + + /* First pass: walk UD chains, compute object sizes that + can be computed. osi.reexamine bitmap at the end will + contain what variables were found in dependency cycles + and therefore need to be reexamined. */ + osi.pass = 0; + osi.changed = false; + collect_object_sizes_for (&osi, ptr); + + /* Second pass: keep recomputing object sizes of variables + that need reexamination, until no object sizes are + increased or all object sizes are computed. */ + if (! bitmap_empty_p (osi.reexamine)) + { + bitmap reexamine = BITMAP_ALLOC (NULL); + + /* If looking for minimum instead of maximum object size, + detect cases where a pointer is increased in a loop. + Although even without this detection pass 2 would eventually + terminate, it could take a long time. If a pointer is + increasing this way, we need to assume 0 object size. + E.g. p = &buf[0]; while (cond) p = p + 4; */ + if (object_size_type & 2) + { + osi.depths = xcalloc (num_ssa_names, sizeof (unsigned int)); + osi.stack = xmalloc (num_ssa_names * sizeof (unsigned int)); + osi.tos = osi.stack; + osi.pass = 1; + /* collect_object_sizes_for is changing + osi.reexamine bitmap, so iterate over a copy. */ + bitmap_copy (reexamine, osi.reexamine); + EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) + if (bitmap_bit_p (osi.reexamine, i)) + check_for_plus_in_loops (&osi, ssa_name (i)); + + free (osi.depths); + osi.depths = NULL; + free (osi.stack); + osi.stack = NULL; + osi.tos = NULL; + } + + do + { + osi.pass = 2; + osi.changed = false; + /* collect_object_sizes_for is changing + osi.reexamine bitmap, so iterate over a copy. */ + bitmap_copy (reexamine, osi.reexamine); + EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) + if (bitmap_bit_p (osi.reexamine, i)) + { + collect_object_sizes_for (&osi, ssa_name (i)); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Reexamining "); + print_generic_expr (dump_file, ssa_name (i), + dump_flags); + fprintf (dump_file, "\n"); + } + } + } + while (osi.changed); + + BITMAP_FREE (reexamine); + } + EXECUTE_IF_SET_IN_BITMAP (osi.reexamine, 0, i, bi) + bitmap_set_bit (computed[object_size_type], i); + + /* Debugging dumps. */ + if (dump_file) + { + EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi) + if (object_sizes[object_size_type][i] + != unknown[object_size_type]) + { + print_generic_expr (dump_file, ssa_name (i), + dump_flags); + fprintf (dump_file, + ": %s %sobject size " + HOST_WIDE_INT_PRINT_UNSIGNED "\n", + (object_size_type & 2) ? "minimum" : "maximum", + (object_size_type & 1) ? "sub" : "", + object_sizes[object_size_type][i]); + } + } + + BITMAP_FREE (osi.reexamine); + BITMAP_FREE (osi.visited); + } + + return object_sizes[object_size_type][SSA_NAME_VERSION (ptr)]; + } + + return unknown[object_size_type]; +} + + +/* Compute object_sizes for PTR, defined to VALUE, which is not + a SSA_NAME. */ + +static void +expr_object_size (struct object_size_info *osi, tree ptr, tree value) +{ + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (ptr); + unsigned HOST_WIDE_INT bytes; + + gcc_assert (object_sizes[object_size_type][varno] + != unknown[object_size_type]); + gcc_assert (osi->pass == 0); + + if (TREE_CODE (value) == WITH_SIZE_EXPR) + value = TREE_OPERAND (value, 0); + + /* Pointer variables should have been handled by merge_object_sizes. */ + gcc_assert (TREE_CODE (value) != SSA_NAME + || !POINTER_TYPE_P (TREE_TYPE (value))); + + if (TREE_CODE (value) == ADDR_EXPR) + bytes = addr_object_size (value, object_size_type); + else if (TREE_CODE (value) == CALL_EXPR) + bytes = alloc_object_size (value, object_size_type); + else + bytes = unknown[object_size_type]; + + if ((object_size_type & 2) == 0) + { + if (object_sizes[object_size_type][varno] < bytes) + object_sizes[object_size_type][varno] = bytes; + } + else + { + if (object_sizes[object_size_type][varno] > bytes) + object_sizes[object_size_type][varno] = bytes; + } +} + + +/* Merge object sizes of ORIG + OFFSET into DEST. Return true if + the object size might need reexamination later. */ + +static bool +merge_object_sizes (struct object_size_info *osi, tree dest, tree orig, + unsigned HOST_WIDE_INT offset) +{ + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (dest); + unsigned HOST_WIDE_INT orig_bytes; + + if (object_sizes[object_size_type][varno] == unknown[object_size_type]) + return false; + if (offset >= offset_limit) + { + object_sizes[object_size_type][varno] = unknown[object_size_type]; + return false; + } + + if (osi->pass == 0) + collect_object_sizes_for (osi, orig); + + orig_bytes = object_sizes[object_size_type][SSA_NAME_VERSION (orig)]; + if (orig_bytes != unknown[object_size_type]) + orig_bytes = (offset > orig_bytes) + ? (unsigned HOST_WIDE_INT) 0 : orig_bytes - offset; + + if ((object_size_type & 2) == 0) + { + if (object_sizes[object_size_type][varno] < orig_bytes) + { + object_sizes[object_size_type][varno] = orig_bytes; + osi->changed = true; + } + } + else + { + if (object_sizes[object_size_type][varno] > orig_bytes) + { + object_sizes[object_size_type][varno] = orig_bytes; + osi->changed = true; + } + } + return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig)); +} + + +/* Compute object_sizes for PTR, defined to VALUE, which is + a PLUS_EXPR. Return true if the object size might need reexamination + later. */ + +static bool +plus_expr_object_size (struct object_size_info *osi, tree var, tree value) +{ + tree op0 = TREE_OPERAND (value, 0); + tree op1 = TREE_OPERAND (value, 1); + bool ptr1_p = POINTER_TYPE_P (TREE_TYPE (op0)) + && TREE_CODE (op0) != INTEGER_CST; + bool ptr2_p = POINTER_TYPE_P (TREE_TYPE (op1)) + && TREE_CODE (op1) != INTEGER_CST; + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (var); + unsigned HOST_WIDE_INT bytes; + + gcc_assert (TREE_CODE (value) == PLUS_EXPR); + + if (object_sizes[object_size_type][varno] == unknown[object_size_type]) + return false; + + /* Swap operands if needed. */ + if (ptr2_p && !ptr1_p) + { + tree tem = op0; + op0 = op1; + op1 = tem; + ptr1_p = true; + ptr2_p = false; + } + + /* Handle PTR + OFFSET here. */ + if (ptr1_p + && !ptr2_p + && TREE_CODE (op1) == INTEGER_CST + && (TREE_CODE (op0) == SSA_NAME + || TREE_CODE (op0) == ADDR_EXPR)) + { + if (! host_integerp (op1, 1)) + bytes = unknown[object_size_type]; + else if (TREE_CODE (op0) == SSA_NAME) + return merge_object_sizes (osi, var, op0, tree_low_cst (op1, 1)); + else + { + unsigned HOST_WIDE_INT off = tree_low_cst (op1, 1); + + bytes = compute_builtin_object_size (value, object_size_type); + if (off > offset_limit) + bytes = unknown[object_size_type]; + else if (off > bytes) + bytes = 0; + else + bytes -= off; + } + } + else + bytes = unknown[object_size_type]; + + if ((object_size_type & 2) == 0) + { + if (object_sizes[object_size_type][varno] < bytes) + object_sizes[object_size_type][varno] = bytes; + } + else + { + if (object_sizes[object_size_type][varno] > bytes) + object_sizes[object_size_type][varno] = bytes; + } + return false; +} + + +/* Compute object sizes for VAR. + For ADDR_EXPR an object size is the number of remaining bytes + to the end of the object (where what is consindered an object depends on + OSI->object_size_type). + For allocation CALL_EXPR like malloc or calloc object size is the size + of the allocation. + For pointer PLUS_EXPR where second operand is a constant integer, + object size is object size of the first operand minus the constant. + If the constant is bigger than the number of remaining bytes until the + end of the object, object size is 0, but if it is instead a pointer + subtraction, object size is unknown[object_size_type]. + To differentiate addition from subtraction, ADDR_EXPR returns + unknown[object_size_type] for all objects bigger than half of the address + space, and constants less than half of the address space are considered + addition, while bigger constants subtraction. + For a memcpy like CALL_EXPR that always returns one of its arguments, the + object size is object size of that argument. + Otherwise, object size is the maximum of object sizes of variables + that it might be set to. */ + +static void +collect_object_sizes_for (struct object_size_info *osi, tree var) +{ + int object_size_type = osi->object_size_type; + unsigned int varno = SSA_NAME_VERSION (var); + tree stmt; + bool reexamine; + + if (bitmap_bit_p (computed[object_size_type], varno)) + return; + + if (osi->pass == 0) + { + if (! bitmap_bit_p (osi->visited, varno)) + { + bitmap_set_bit (osi->visited, varno); + object_sizes[object_size_type][varno] + = (object_size_type & 2) ? -1 : 0; + } + else + { + /* Found a dependency loop. Mark the variable for later + re-examination. */ + bitmap_set_bit (osi->reexamine, varno); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Found a dependency loop at "); + print_generic_expr (dump_file, var, dump_flags); + fprintf (dump_file, "\n"); + } + return; + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Visiting use-def links for "); + print_generic_expr (dump_file, var, dump_flags); + fprintf (dump_file, "\n"); + } + + stmt = SSA_NAME_DEF_STMT (var); + reexamine = false; + + switch (TREE_CODE (stmt)) + { + case RETURN_EXPR: + if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR) + abort (); + stmt = TREE_OPERAND (stmt, 0); + /* FALLTHRU */ + + case MODIFY_EXPR: + { + tree rhs = TREE_OPERAND (stmt, 1), arg; + STRIP_NOPS (rhs); + + if (TREE_CODE (rhs) == CALL_EXPR) + { + arg = pass_through_call (rhs); + if (arg) + rhs = arg; + } + + if (TREE_CODE (rhs) == SSA_NAME + && POINTER_TYPE_P (TREE_TYPE (rhs))) + reexamine = merge_object_sizes (osi, var, rhs, 0); + + else if (TREE_CODE (rhs) == PLUS_EXPR) + reexamine = plus_expr_object_size (osi, var, rhs); + + else + expr_object_size (osi, var, rhs); + break; + } + + case ASM_EXPR: + /* Pointers defined by __asm__ statements can point anywhere. */ + object_sizes[object_size_type][varno] = unknown[object_size_type]; + break; + + case NOP_EXPR: + { + tree decl = SSA_NAME_VAR (var); + + gcc_assert (IS_EMPTY_STMT (stmt)); + + if (TREE_CODE (decl) != PARM_DECL && DECL_INITIAL (decl)) + expr_object_size (osi, var, DECL_INITIAL (decl)); + else + expr_object_size (osi, var, decl); + } + break; + + case PHI_NODE: + { + int i; + + for (i = 0; i < PHI_NUM_ARGS (stmt); i++) + { + tree rhs = PHI_ARG_DEF (stmt, i); + + if (object_sizes[object_size_type][varno] + == unknown[object_size_type]) + break; + + if (TREE_CODE (rhs) == SSA_NAME) + reexamine |= merge_object_sizes (osi, var, rhs, 0); + else if (osi->pass == 0) + expr_object_size (osi, var, rhs); + } + break; + } + default: + gcc_unreachable (); + } + + if (! reexamine + || object_sizes[object_size_type][varno] == unknown[object_size_type]) + { + bitmap_set_bit (computed[object_size_type], varno); + bitmap_clear_bit (osi->reexamine, varno); + } + else + { + bitmap_set_bit (osi->reexamine, varno); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Need to reexamine "); + print_generic_expr (dump_file, var, dump_flags); + fprintf (dump_file, "\n"); + } + } +} + + +/* Helper function for check_for_plus_in_loops. Called recursively + to detect loops. */ + +static void +check_for_plus_in_loops_1 (struct object_size_info *osi, tree var, + unsigned int depth) +{ + tree stmt = SSA_NAME_DEF_STMT (var); + unsigned int varno = SSA_NAME_VERSION (var); + + if (osi->depths[varno]) + { + if (osi->depths[varno] != depth) + { + unsigned int *sp; + + /* Found a loop involving pointer addition. */ + for (sp = osi->tos; sp > osi->stack; ) + { + --sp; + bitmap_clear_bit (osi->reexamine, *sp); + bitmap_set_bit (computed[osi->object_size_type], *sp); + object_sizes[osi->object_size_type][*sp] = 0; + if (*sp == varno) + break; + } + } + return; + } + else if (! bitmap_bit_p (osi->reexamine, varno)) + return; + + osi->depths[varno] = depth; + *osi->tos++ = varno; + + switch (TREE_CODE (stmt)) + { + case RETURN_EXPR: + if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR) + abort (); + stmt = TREE_OPERAND (stmt, 0); + /* FALLTHRU */ + + case MODIFY_EXPR: + { + tree rhs = TREE_OPERAND (stmt, 1), arg; + STRIP_NOPS (rhs); + + if (TREE_CODE (rhs) == CALL_EXPR) + { + arg = pass_through_call (rhs); + if (arg) + rhs = arg; + } + + if (TREE_CODE (rhs) == SSA_NAME) + check_for_plus_in_loops_1 (osi, rhs, depth); + else if (TREE_CODE (rhs) == PLUS_EXPR) + { + tree op0 = TREE_OPERAND (rhs, 0); + tree op1 = TREE_OPERAND (rhs, 1); + tree cst, basevar; + + if (TREE_CODE (op0) == SSA_NAME) + { + basevar = op0; + cst = op1; + } + else + { + basevar = op1; + cst = op0; + gcc_assert (TREE_CODE (basevar) == SSA_NAME); + } + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + + check_for_plus_in_loops_1 (osi, basevar, + depth + !integer_zerop (cst)); + } + else + gcc_unreachable (); + break; + } + case PHI_NODE: + { + int i; + + for (i = 0; i < PHI_NUM_ARGS (stmt); i++) + { + tree rhs = PHI_ARG_DEF (stmt, i); + + if (TREE_CODE (rhs) == SSA_NAME) + check_for_plus_in_loops_1 (osi, rhs, depth); + } + break; + } + default: + gcc_unreachable (); + } + + osi->depths[varno] = 0; + osi->tos--; +} + + +/* Check if some pointer we are computing object size of is being increased + within a loop. If yes, assume all the SSA variables participating in + that loop have minimum object sizes 0. */ + +static void +check_for_plus_in_loops (struct object_size_info *osi, tree var) +{ + tree stmt = SSA_NAME_DEF_STMT (var); + + switch (TREE_CODE (stmt)) + { + case RETURN_EXPR: + if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR) + abort (); + stmt = TREE_OPERAND (stmt, 0); + /* FALLTHRU */ + + case MODIFY_EXPR: + { + tree rhs = TREE_OPERAND (stmt, 1), arg; + STRIP_NOPS (rhs); + + if (TREE_CODE (rhs) == CALL_EXPR) + { + arg = pass_through_call (rhs); + if (arg) + rhs = arg; + } + + if (TREE_CODE (rhs) == PLUS_EXPR) + { + tree op0 = TREE_OPERAND (rhs, 0); + tree op1 = TREE_OPERAND (rhs, 1); + tree cst, basevar; + + if (TREE_CODE (op0) == SSA_NAME) + { + basevar = op0; + cst = op1; + } + else + { + basevar = op1; + cst = op0; + gcc_assert (TREE_CODE (basevar) == SSA_NAME); + } + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + + if (integer_zerop (cst)) + break; + + osi->depths[SSA_NAME_VERSION (basevar)] = 1; + *osi->tos++ = SSA_NAME_VERSION (basevar); + check_for_plus_in_loops_1 (osi, var, 2); + osi->depths[SSA_NAME_VERSION (basevar)] = 0; + osi->tos--; + } + break; + } + default: + break; + } +} + + +/* Initialize data structures for the object size computation. */ + +void +init_object_sizes (void) +{ + int object_size_type; + + if (object_sizes[0]) + return; + + for (object_size_type = 0; object_size_type <= 3; object_size_type++) + { + object_sizes[object_size_type] + = xmalloc (num_ssa_names * sizeof (HOST_WIDE_INT)); + computed[object_size_type] = BITMAP_ALLOC (NULL); + } + + init_offset_limit (); +} + + +/* Destroy data structures after the object size computation. */ + +void +fini_object_sizes (void) +{ + int object_size_type; + + for (object_size_type = 0; object_size_type <= 3; object_size_type++) + { + free (object_sizes[object_size_type]); + BITMAP_FREE (computed[object_size_type]); + object_sizes[object_size_type] = NULL; + } +} + + +/* Simple pass to optimize all __builtin_object_size () builtins. */ + +static void +compute_object_sizes (void) +{ + basic_block bb; + FOR_EACH_BB (bb) + { + block_stmt_iterator i; + for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) + { + tree *stmtp = bsi_stmt_ptr (i); + tree call = get_rhs (*stmtp); + tree callee, result; + + if (!call || TREE_CODE (call) != CALL_EXPR) + continue; + + callee = get_callee_fndecl (call); + if (!callee + || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL + || DECL_FUNCTION_CODE (callee) != BUILT_IN_OBJECT_SIZE) + continue; + + init_object_sizes (); + result = fold_builtin (callee, TREE_OPERAND (call, 1), false); + if (!result) + { + tree arglist = TREE_OPERAND (call, 1); + + if (arglist != NULL + && POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))) + && TREE_CHAIN (arglist) != NULL + && TREE_CHAIN (TREE_CHAIN (arglist)) == NULL) + { + tree ost = TREE_VALUE (TREE_CHAIN (arglist)); + + if (host_integerp (ost, 1)) + { + unsigned HOST_WIDE_INT object_size_type + = tree_low_cst (ost, 1); + + if (object_size_type < 2) + result = fold_convert (size_type_node, + integer_minus_one_node); + else if (object_size_type < 4) + result = size_zero_node; + } + } + + if (!result) + continue; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Simplified\n "); + print_generic_stmt (dump_file, *stmtp, dump_flags); + } + + if (!set_rhs (stmtp, result)) + abort (); + update_stmt (*stmtp); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "to\n "); + print_generic_stmt (dump_file, *stmtp, dump_flags); + fprintf (dump_file, "\n"); + } + } + } + + fini_object_sizes (); +} + +struct tree_opt_pass pass_object_sizes = +{ + "objsz", /* name */ + NULL, /* gate */ + compute_object_sizes, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_cfg | PROP_ssa | PROP_alias, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_verify_ssa, /* todo_flags_finish */ + 0 /* letter */ +}; diff --git a/gcc/tree-optimize.c b/gcc/tree-optimize.c index 0e477de528f..4b46c9b7ec5 100644 --- a/gcc/tree-optimize.c +++ b/gcc/tree-optimize.c @@ -435,6 +435,7 @@ init_tree_optimization_passes (void) NEXT_PASS (pass_may_alias); NEXT_PASS (pass_forwprop); NEXT_PASS (pass_phiopt); + NEXT_PASS (pass_object_sizes); NEXT_PASS (pass_store_ccp); NEXT_PASS (pass_store_copy_prop); NEXT_PASS (pass_fold_builtins); diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index d6eaaa997da..471747bf73c 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -196,6 +196,7 @@ extern struct tree_opt_pass pass_lower_complex_O0; extern struct tree_opt_pass pass_lower_complex; extern struct tree_opt_pass pass_lower_vector; extern struct tree_opt_pass pass_lower_vector_ssa; +extern struct tree_opt_pass pass_object_sizes; extern struct tree_opt_pass pass_fold_builtins; extern struct tree_opt_pass pass_stdarg; extern struct tree_opt_pass pass_early_warn_uninitialized; diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c index 71385ddeffd..8bf824250e4 100644 --- a/gcc/tree-ssa-ccp.c +++ b/gcc/tree-ssa-ccp.c @@ -1965,24 +1965,49 @@ fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data) } -/* Return the string length of ARG in LENGTH. If ARG is an SSA name variable, - follow its use-def chains. If LENGTH is not NULL and its value is not - equal to the length we determine, or if we are unable to determine the - length, return false. VISITED is a bitmap of visited variables. */ +/* Return the string length, maximum string length or maximum value of + ARG in LENGTH. + If ARG is an SSA name variable, follow its use-def chains. If LENGTH + is not NULL and, for TYPE == 0, its value is not equal to the length + we determine or if we are unable to determine the length or value, + return false. VISITED is a bitmap of visited variables. + TYPE is 0 if string length should be returned, 1 for maximum string + length and 2 for maximum value ARG can have. */ static bool -get_strlen (tree arg, tree *length, bitmap visited) +get_maxval_strlen (tree arg, tree *length, bitmap visited, int type) { tree var, def_stmt, val; if (TREE_CODE (arg) != SSA_NAME) { - val = c_strlen (arg, 1); + if (type == 2) + { + val = arg; + if (TREE_CODE (val) != INTEGER_CST + || tree_int_cst_sgn (val) < 0) + return false; + } + else + val = c_strlen (arg, 1); if (!val) return false; - if (*length && simple_cst_equal (val, *length) != 1) - return false; + if (*length) + { + if (type > 0) + { + if (TREE_CODE (*length) != INTEGER_CST + || TREE_CODE (val) != INTEGER_CST) + return false; + + if (tree_int_cst_lt (*length, val)) + *length = val; + return true; + } + else if (simple_cst_equal (val, *length) != 1) + return false; + } *length = val; return true; @@ -2000,28 +2025,14 @@ get_strlen (tree arg, tree *length, bitmap visited) { case MODIFY_EXPR: { - tree len, rhs; - + tree rhs; + /* The RHS of the statement defining VAR must either have a constant length or come from another SSA_NAME with a constant length. */ rhs = TREE_OPERAND (def_stmt, 1); STRIP_NOPS (rhs); - if (TREE_CODE (rhs) == SSA_NAME) - return get_strlen (rhs, length, visited); - - /* See if the RHS is a constant length. */ - len = c_strlen (rhs, 1); - if (len) - { - if (*length && simple_cst_equal (len, *length) != 1) - return false; - - *length = len; - return true; - } - - break; + return get_maxval_strlen (rhs, length, visited, type); } case PHI_NODE: @@ -2043,7 +2054,7 @@ get_strlen (tree arg, tree *length, bitmap visited) if (arg == PHI_RESULT (def_stmt)) continue; - if (!get_strlen (arg, length, visited)) + if (!get_maxval_strlen (arg, length, visited, type)) return false; } @@ -2065,9 +2076,9 @@ get_strlen (tree arg, tree *length, bitmap visited) static tree ccp_fold_builtin (tree stmt, tree fn) { - tree result, strlen_val[2]; + tree result, val[3]; tree callee, arglist, a; - int strlen_arg, i; + int arg_mask, i, type; bitmap visited; bool ignore; @@ -2079,11 +2090,11 @@ ccp_fold_builtin (tree stmt, tree fn) arglist = TREE_OPERAND (fn, 1); result = fold_builtin (callee, arglist, ignore); if (result) - { - if (ignore) - STRIP_NOPS (result); - return result; - } + { + if (ignore) + STRIP_NOPS (result); + return result; + } /* Ignore MD builtins. */ if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD) @@ -2100,11 +2111,31 @@ ccp_fold_builtin (tree stmt, tree fn) case BUILT_IN_STRLEN: case BUILT_IN_FPUTS: case BUILT_IN_FPUTS_UNLOCKED: - strlen_arg = 1; + arg_mask = 1; + type = 0; break; case BUILT_IN_STRCPY: case BUILT_IN_STRNCPY: - strlen_arg = 2; + arg_mask = 2; + type = 0; + break; + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + case BUILT_IN_STRNCPY_CHK: + arg_mask = 4; + type = 2; + break; + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + arg_mask = 2; + type = 1; + break; + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + arg_mask = 2; + type = 2; break; default: return NULL_TREE; @@ -2113,15 +2144,15 @@ ccp_fold_builtin (tree stmt, tree fn) /* Try to use the dataflow information gathered by the CCP process. */ visited = BITMAP_ALLOC (NULL); - memset (strlen_val, 0, sizeof (strlen_val)); + memset (val, 0, sizeof (val)); for (i = 0, a = arglist; - strlen_arg; - i++, strlen_arg >>= 1, a = TREE_CHAIN (a)) - if (strlen_arg & 1) + arg_mask; + i++, arg_mask >>= 1, a = TREE_CHAIN (a)) + if (arg_mask & 1) { bitmap_clear (visited); - if (!get_strlen (TREE_VALUE (a), &strlen_val[i], visited)) - strlen_val[i] = NULL_TREE; + if (!get_maxval_strlen (TREE_VALUE (a), &val[i], visited, type)) + val[i] = NULL_TREE; } BITMAP_FREE (visited); @@ -2130,9 +2161,9 @@ ccp_fold_builtin (tree stmt, tree fn) switch (DECL_FUNCTION_CODE (callee)) { case BUILT_IN_STRLEN: - if (strlen_val[0]) + if (val[0]) { - tree new = fold_convert (TREE_TYPE (fn), strlen_val[0]); + tree new = fold_convert (TREE_TYPE (fn), val[0]); /* If the result is not a valid gimple value, or not a cast of a valid gimple value, then we can not use the result. */ @@ -2144,33 +2175,53 @@ ccp_fold_builtin (tree stmt, tree fn) break; case BUILT_IN_STRCPY: - if (strlen_val[1] && is_gimple_val (strlen_val[1])) - { - tree fndecl = get_callee_fndecl (fn); - tree arglist = TREE_OPERAND (fn, 1); - result = fold_builtin_strcpy (fndecl, arglist, strlen_val[1]); - } + if (val[1] && is_gimple_val (val[1])) + result = fold_builtin_strcpy (callee, arglist, val[1]); break; case BUILT_IN_STRNCPY: - if (strlen_val[1] && is_gimple_val (strlen_val[1])) - { - tree fndecl = get_callee_fndecl (fn); - tree arglist = TREE_OPERAND (fn, 1); - result = fold_builtin_strncpy (fndecl, arglist, strlen_val[1]); - } + if (val[1] && is_gimple_val (val[1])) + result = fold_builtin_strncpy (callee, arglist, val[1]); break; case BUILT_IN_FPUTS: result = fold_builtin_fputs (arglist, TREE_CODE (stmt) != MODIFY_EXPR, 0, - strlen_val[0]); + val[0]); break; case BUILT_IN_FPUTS_UNLOCKED: result = fold_builtin_fputs (arglist, TREE_CODE (stmt) != MODIFY_EXPR, 1, - strlen_val[0]); + val[0]); + break; + + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + if (val[2] && is_gimple_val (val[2])) + result = fold_builtin_memory_chk (callee, arglist, val[2], ignore, + DECL_FUNCTION_CODE (callee)); + break; + + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STPCPY_CHK: + if (val[1] && is_gimple_val (val[1])) + result = fold_builtin_stxcpy_chk (callee, arglist, val[1], ignore, + DECL_FUNCTION_CODE (callee)); + break; + + case BUILT_IN_STRNCPY_CHK: + if (val[2] && is_gimple_val (val[2])) + result = fold_builtin_strncpy_chk (arglist, val[2]); + break; + + case BUILT_IN_SNPRINTF_CHK: + case BUILT_IN_VSNPRINTF_CHK: + if (val[1] && is_gimple_val (val[1])) + result = fold_builtin_snprintf_chk (arglist, val[1], + DECL_FUNCTION_CODE (callee)); break; default: @@ -2338,18 +2389,26 @@ execute_fold_all_builtins (void) FOR_EACH_BB (bb) { block_stmt_iterator i; - for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) + for (i = bsi_start (bb); !bsi_end_p (i); ) { tree *stmtp = bsi_stmt_ptr (i); tree old_stmt = *stmtp; tree call = get_rhs (*stmtp); tree callee, result; + enum built_in_function fcode; if (!call || TREE_CODE (call) != CALL_EXPR) - continue; + { + bsi_next (&i); + continue; + } callee = get_callee_fndecl (call); if (!callee || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL) - continue; + { + bsi_next (&i); + continue; + } + fcode = DECL_FUNCTION_CODE (callee); result = ccp_fold_builtin (*stmtp, call); if (!result) @@ -2363,6 +2422,7 @@ execute_fold_all_builtins (void) break; default: + bsi_next (&i); continue; } @@ -2393,6 +2453,20 @@ execute_fold_all_builtins (void) print_generic_stmt (dump_file, *stmtp, dump_flags); fprintf (dump_file, "\n"); } + + /* Retry the same statement if it changed into another + builtin, there might be new opportunities now. */ + call = get_rhs (*stmtp); + if (!call || TREE_CODE (call) != CALL_EXPR) + { + bsi_next (&i); + continue; + } + callee = get_callee_fndecl (call); + if (!callee + || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL + || DECL_FUNCTION_CODE (callee) == fcode) + bsi_next (&i); } } diff --git a/gcc/tree.h b/gcc/tree.h index 1c0a4be2fcb..a2e9a86f886 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3623,6 +3623,12 @@ extern tree fold_builtin (tree, tree, bool); extern tree fold_builtin_fputs (tree, bool, bool, tree); extern tree fold_builtin_strcpy (tree, tree, tree); extern tree fold_builtin_strncpy (tree, tree, tree); +extern tree fold_builtin_memory_chk (tree, tree, tree, bool, + enum built_in_function); +extern tree fold_builtin_stxcpy_chk (tree, tree, tree, bool, + enum built_in_function); +extern tree fold_builtin_strncpy_chk (tree, tree); +extern tree fold_builtin_snprintf_chk (tree, tree, enum built_in_function); extern bool fold_builtin_next_arg (tree); extern enum built_in_function builtin_mathfn_code (tree); extern tree build_function_call_expr (tree, tree); @@ -4010,4 +4016,9 @@ extern void vect_set_verbosity_level (const char *); extern tree tree_mem_ref_addr (tree, tree); extern void copy_mem_ref_info (tree, tree); +/* In tree-object-size.c. */ +extern void init_object_sizes (void); +extern void fini_object_sizes (void); +extern unsigned HOST_WIDE_INT compute_builtin_object_size (tree, int); + #endif /* GCC_TREE_H */