+2017-11-21 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/82945
+ * builtins.c (expand_builtin_strlen): Call maybe_warn_nonstring_arg.
+ * calls.h (maybe_warn_nonstring_arg): Declare new function.
+ * calls.c (get_attr_nonstring_decl, maybe_warn_nonstring_arg): New
+ functions.
+ (initialize_argument_information): Call maybe_warn_nonstring_arg.
+ * calls.h (get_attr_nonstring_decl): Declare new function.
+ * doc/extend.texi (attribute nonstring): Update.
+ * gimple-fold.c (gimple_fold_builtin_strncpy): Call
+ get_attr_nonstring_decl and handle it.
+ * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Same. Improve
+ detection of nul-termination.
+ (strlen_to_stridx): Change to a pointer.
+ (handle_builtin_strlen, handle_builtin_stxncpy): Adjust.
+ (pass_strlen::execute): Same.
+
2017-11-21 Sergey Shalnov <Sergey.Shalnov@intel.com>
* config/i386/i386-opts.h (enum prefer_vector_width): Added new enum
if (!maybe_expand_insn (icode, 4, ops))
return NULL_RTX;
+ /* Check to see if the argument was declared attribute nonstring
+ and if so, issue a warning since at this point it's not known
+ to be nul-terminated. */
+ maybe_warn_nonstring_arg (TREE_OPERAND (CALL_EXPR_FN (exp), 0), exp);
+
/* Now that we are assured of success, expand the source. */
start_sequence ();
pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL);
}
}
+/* If EXPR refers to a character array or pointer declared attribute
+ nonstring return a decl for that array or pointer and set *REF to
+ the referenced enclosing object or pointer. Otherwise returns
+ null. */
+
+tree
+get_attr_nonstring_decl (tree expr, tree *ref)
+{
+ tree decl = expr;
+ if (TREE_CODE (decl) == SSA_NAME)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (decl);
+
+ if (is_gimple_assign (def))
+ {
+ tree_code code = gimple_assign_rhs_code (def);
+ if (code == ADDR_EXPR
+ || code == COMPONENT_REF
+ || code == VAR_DECL)
+ decl = gimple_assign_rhs1 (def);
+ }
+ else if (tree var = SSA_NAME_VAR (decl))
+ decl = var;
+ }
+
+ if (TREE_CODE (decl) == ADDR_EXPR)
+ decl = TREE_OPERAND (decl, 0);
+
+ if (ref)
+ *ref = decl;
+
+ if (TREE_CODE (decl) == COMPONENT_REF)
+ decl = TREE_OPERAND (decl, 1);
+
+ if (DECL_P (decl)
+ && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
+ return decl;
+
+ return NULL_TREE;
+}
+
+/* Check the size argument to the strncmp built-in to see if it's within
+ the bounds of the arguments and if not, issue a warning. */
+
+static void
+warn_nonstring_bound (tree fndecl, tree call)
+{
+ bool with_bounds = CALL_WITH_BOUNDS_P (call);
+
+ tree cnt = CALL_EXPR_ARG (call, with_bounds ? 4 : 2);
+
+ tree cntrange[2];
+ if (!get_size_range (cnt, cntrange))
+ return;
+
+ location_t callloc = EXPR_LOCATION (call);
+
+ for (unsigned i = 0; i != 2; ++i)
+ {
+ tree str = CALL_EXPR_ARG (call, i + 2 * with_bounds);
+
+ tree sref;
+ tree decl = get_attr_nonstring_decl (str, &sref);
+ if (!decl)
+ continue;
+
+ tree type = TREE_TYPE (decl);
+ if (TREE_CODE (type) != ARRAY_TYPE)
+ continue;
+
+ tree dom = TYPE_DOMAIN (type);
+ if (!dom)
+ continue;
+
+ tree bound = TYPE_MAX_VALUE (dom);
+ if (!bound)
+ continue;
+
+ bool warned = false;
+ if (tree_int_cst_le (bound, cntrange[0]))
+ warned = warning_at (callloc, OPT_Wstringop_truncation,
+ "%qD argument %i declared attribute %<nonstring%> "
+ "is smaller than the specified bound %E",
+ fndecl, i, cntrange[0]);
+ if (warned)
+ {
+ location_t loc = DECL_SOURCE_LOCATION (decl);
+ if (loc != UNKNOWN_LOCATION)
+ inform (loc, "argument %qD declared here", decl);
+ }
+ }
+}
+
+/* Warn about passing a non-string array/pointer to a function that
+ expects a nul-terminated string argument. */
+
+void
+maybe_warn_nonstring_arg (tree fndecl, tree exp)
+{
+ if (!fndecl || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+ return;
+
+ bool with_bounds = CALL_WITH_BOUNDS_P (exp);
+
+ /* The bound argument to a bounded string function like strncpy. */
+ tree bound = NULL_TREE;
+
+ /* It's safe to call "bounded" string functions with a non-string
+ argument since the functions provide an explicit bound for this
+ purpose. */
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_STPNCPY:
+ case BUILT_IN_STPNCPY_CHK:
+ case BUILT_IN_STRNCMP:
+ case BUILT_IN_STRNCASECMP:
+ case BUILT_IN_STRNCPY:
+ case BUILT_IN_STRNCPY_CHK:
+ bound = CALL_EXPR_ARG (exp, with_bounds ? 4 : 2);
+ break;
+
+ case BUILT_IN_STRNDUP:
+ bound = CALL_EXPR_ARG (exp, with_bounds ? 2 : 1);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Determine the range of the bound argument (if specified). */
+ tree bndrng[2] = { NULL_TREE, NULL_TREE };
+ if (bound)
+ get_size_range (bound, bndrng);
+
+ /* Iterate over the built-in function's formal arguments and check
+ each const char* against the actual argument. If the actual
+ argument is declared attribute non-string issue a warning unless
+ the argument's maximum length is bounded. */
+ function_args_iterator it;
+ function_args_iter_init (&it, TREE_TYPE (fndecl));
+
+ for (unsigned argno = 0; ; ++argno, function_args_iter_next (&it))
+ {
+ tree argtype = function_args_iter_cond (&it);
+ if (!argtype)
+ break;
+
+ if (TREE_CODE (argtype) != POINTER_TYPE)
+ continue;
+
+ argtype = TREE_TYPE (argtype);
+
+ if (TREE_CODE (argtype) != INTEGER_TYPE
+ || !TYPE_READONLY (argtype))
+ continue;
+
+ argtype = TYPE_MAIN_VARIANT (argtype);
+ if (argtype != char_type_node)
+ continue;
+
+ tree callarg = CALL_EXPR_ARG (exp, argno);
+ if (TREE_CODE (callarg) == ADDR_EXPR)
+ callarg = TREE_OPERAND (callarg, 0);
+
+ /* See if the destination is declared with attribute "nonstring". */
+ tree decl = get_attr_nonstring_decl (callarg);
+ if (!decl)
+ continue;
+
+ tree type = TREE_TYPE (decl);
+
+ offset_int wibnd = 0;
+ if (bndrng[0])
+ wibnd = wi::to_offset (bndrng[0]);
+
+ offset_int asize = wibnd;
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ if (tree arrbnd = TYPE_DOMAIN (type))
+ {
+ if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
+ asize = wi::to_offset (arrbnd) + 1;
+ }
+
+ location_t loc = EXPR_LOCATION (exp);
+
+ bool warned = false;
+
+ if (wi::ltu_p (asize, wibnd))
+ warned = warning_at (loc, OPT_Wstringop_overflow_,
+ "%qD argument %i declared attribute %<nonstring%> "
+ "is smaller than the specified bound %E",
+ fndecl, argno + 1, bndrng[0]);
+ else if (!bound)
+ warned = warning_at (loc, OPT_Wstringop_overflow_,
+ "%qD argument %i declared attribute %<nonstring%>",
+ fndecl, argno + 1);
+
+ if (warned)
+ inform (DECL_SOURCE_LOCATION (decl),
+ "argument %qD declared here", decl);
+ }
+}
+
/* Issue an error if CALL_EXPR was flagged as requiring
tall-call optimization. */
alloc_size. */
maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx);
}
+
+ /* Detect passing non-string arguments to functions expecting
+ nul-terminated strings. */
+ maybe_warn_nonstring_arg (fndecl, exp);
}
/* Update ARGS_SIZE to contain the total size for the argument block.
tree, bool);
extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
extern bool get_size_range (tree, tree[2]);
+extern tree get_attr_nonstring_decl (tree, tree * = NULL);
+extern void maybe_warn_nonstring_arg (tree, tree);
#endif // GCC_CALLS_H
The @code{nonstring} variable attribute specifies that an object or member
declaration with type array of @code{char} or pointer to @code{char} is
intended to store character arrays that do not necessarily contain
-a terminating @code{NUL} character. This is useful to avoid warnings
-when such an array or pointer is used as an argument to a bounded string
-manipulation function such as @code{strncpy}. For example, without the
-attribute, GCC will issue a warning for the call below because it may
-truncate the copy without appending the terminating NUL character. Using
-the attribute makes it possible to suppress the warning.
+a terminating @code{NUL} character. This is useful in detecting uses
+of such arrays or pointers with functions that expect NUL-terminated
+strings, and to avoid warnings when such an array or pointer is used
+as an argument to a bounded string manipulation function such as
+@code{strncpy}. For example, without the attribute, GCC will issue
+a warning for the @code{strncpy} call below because it may truncate
+the copy without appending the terminating @code{NUL} character. Using
+the attribute makes it possible to suppress the warning. However, when
+the array is declared with the attribute the call to @code{strlen} is
+diagnosed because when the array doesn't contain a @code{NUL}-terminated
+string the call is undefined. To copy, compare, of search non-string
+character arrays use the @code{memcpy}, @code{memcmp}, @code{memchr},
+and other functions that operate on arrays of bytes. In addition,
+calling @code{strnlen} and @code{strndup} with such arrays is safe
+provided a suitable bound is specified, and not diagnosed.
@smallexample
struct Data
@{
char name [32] __attribute__ ((nonstring));
@};
-void f (struct Data *pd, const char *s)
+
+int f (struct Data *pd, const char *s)
@{
strncpy (pd->name, s, sizeof pd->name);
@dots{}
+ return strlen (pd->name); // unsafe, gets a warning
@}
@end smallexample
#include "asan.h"
#include "diagnostic-core.h"
#include "intl.h"
+#include "calls.h"
/* Return true when DECL can be referenced from current unit.
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
{
gimple *stmt = gsi_stmt (*gsi);
location_t loc = gimple_location (stmt);
+ bool nonstring = get_attr_nonstring_decl (dest) != NULL_TREE;
/* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
{
- tree fndecl = gimple_call_fndecl (stmt);
- gcall *call = as_a <gcall *> (stmt);
-
- /* Warn about the lack of nul termination: the result is not
- a (nul-terminated) string. */
- tree slen = get_maxval_strlen (src, 0);
- if (slen && !integer_zerop (slen))
- warning_at (loc, OPT_Wstringop_truncation,
- "%G%qD destination unchanged after copying no bytes "
- "from a string of length %E",
- call, fndecl, slen);
- else
- warning_at (loc, OPT_Wstringop_truncation,
- "%G%qD destination unchanged after copying no bytes",
- call, fndecl);
+ /* Avoid warning if the destination refers to a an array/pointer
+ decorate with attribute nonstring. */
+ if (!nonstring)
+ {
+ tree fndecl = gimple_call_fndecl (stmt);
+ gcall *call = as_a <gcall *> (stmt);
+
+ /* Warn about the lack of nul termination: the result is not
+ a (nul-terminated) string. */
+ tree slen = get_maxval_strlen (src, 0);
+ if (slen && !integer_zerop (slen))
+ warning_at (loc, OPT_Wstringop_truncation,
+ "%G%qD destination unchanged after copying no bytes "
+ "from a string of length %E",
+ call, fndecl, slen);
+ else
+ warning_at (loc, OPT_Wstringop_truncation,
+ "%G%qD destination unchanged after copying no bytes",
+ call, fndecl);
+ }
replace_call_with_value (gsi, dest);
return true;
if (tree_int_cst_lt (ssize, len))
return false;
- if (tree_int_cst_lt (len, slen))
- {
- tree fndecl = gimple_call_fndecl (stmt);
- gcall *call = as_a <gcall *> (stmt);
-
- warning_at (loc, OPT_Wstringop_truncation,
- (tree_int_cst_equal (size_one_node, len)
- ? G_("%G%qD output truncated copying %E byte "
- "from a string of length %E")
- : G_("%G%qD output truncated copying %E bytes "
- "from a string of length %E")),
- call, fndecl, len, slen);
- }
- else if (tree_int_cst_equal (len, slen))
+ if (!nonstring)
{
- tree decl = dest;
- if (TREE_CODE (decl) == SSA_NAME)
+ if (tree_int_cst_lt (len, slen))
{
- gimple *def_stmt = SSA_NAME_DEF_STMT (decl);
- if (is_gimple_assign (def_stmt))
- {
- tree_code code = gimple_assign_rhs_code (def_stmt);
- if (code == ADDR_EXPR || code == VAR_DECL)
- decl = gimple_assign_rhs1 (def_stmt);
- }
+ tree fndecl = gimple_call_fndecl (stmt);
+ gcall *call = as_a <gcall *> (stmt);
+
+ warning_at (loc, OPT_Wstringop_truncation,
+ (tree_int_cst_equal (size_one_node, len)
+ ? G_("%G%qD output truncated copying %E byte "
+ "from a string of length %E")
+ : G_("%G%qD output truncated copying %E bytes "
+ "from a string of length %E")),
+ call, fndecl, len, slen);
+ }
+ else if (tree_int_cst_equal (len, slen))
+ {
+ tree fndecl = gimple_call_fndecl (stmt);
+ gcall *call = as_a <gcall *> (stmt);
+
+ warning_at (loc, OPT_Wstringop_truncation,
+ (tree_int_cst_equal (size_one_node, len)
+ ? G_("%G%qD output truncated before terminating nul "
+ "copying %E byte from a string of the same "
+ "length")
+ : G_("%G%qD output truncated before terminating nul "
+ "copying %E bytes from a string of the same "
+ "length")),
+ call, fndecl, len);
}
-
- if (TREE_CODE (decl) == ADDR_EXPR)
- decl = TREE_OPERAND (decl, 0);
-
- if (TREE_CODE (decl) == COMPONENT_REF)
- decl = TREE_OPERAND (decl, 1);
-
- tree fndecl = gimple_call_fndecl (stmt);
- gcall *call = as_a <gcall *> (stmt);
-
- if (!DECL_P (decl)
- || !lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
- warning_at (loc, OPT_Wstringop_truncation,
- (tree_int_cst_equal (size_one_node, len)
- ? G_("%G%qD output truncated before terminating nul "
- "copying %E byte from a string of the same "
- "length")
- : G_("%G%qD output truncated before terminating nul "
- "copying %E bytes from a string of the same "
- "length")),
- call, fndecl, len);
}
/* OK transform into builtin memcpy. */
+2017-11-21 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/82945
+ * c-c++-common/Wstringop-truncation-2.c: New test.
+ * c-c++-common/Wstringop-truncation.c: Adjust.
+ * c-c++-common/attr-nonstring-2.c: Adjust.
+ * c-c++-common/attr-nonstring-3.c: New test.
+
2017-11-21 Sergey Shalnov <Sergey.Shalnov@intel.com>
* g++.dg/ext/pr57362.C (__attribute__): Test
--- /dev/null
+/* Verify that
+ { dg-do compile }
+ { dg-options "-O2 -Wstringop-truncation -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+#define stpncpy(d, s, n) __builtin_stpncpy ((d), (s), (n))
+#define strncpy(d, s, n) __builtin_stpncpy ((d), (s), (n))
+
+void sink (void*);
+
+struct A {
+ char arr[3] __attribute__ ((nonstring));
+ char str[3];
+};
+
+struct B { struct A a[3]; int i; };
+struct C { struct B b[3]; int i; };
+
+void stpncpy_arr_1 (struct C *pc, const char *s)
+{
+ stpncpy (pc->b[0].a[0].arr, s, sizeof pc->b[0].a[0].arr);
+ sink (pc->b[0].a[0].arr);
+
+ stpncpy (pc->b[0].a[1].arr, s, sizeof pc->b[0].a[1].arr);
+ sink (pc->b[0].a[1].arr);
+
+ stpncpy (pc->b[0].a[2].arr, s, sizeof pc->b[0].a[2].arr);
+ sink (pc->b[0].a[2].arr);
+
+ stpncpy (pc->b[1].a[0].arr, s, sizeof pc->b[1].a[0].arr);
+ sink (pc->b[1].a[0].arr);
+
+ stpncpy (pc->b[1].a[1].arr, s, sizeof pc->b[1].a[1].arr);
+ sink (pc->b[1].a[1].arr);
+
+ stpncpy (pc->b[1].a[2].arr, s, sizeof pc->b[1].a[2].arr);
+ sink (pc->b[1].a[2].arr);
+
+ stpncpy (pc->b[2].a[0].arr, s, sizeof pc->b[2].a[0].arr);
+ sink (pc->b[2].a[0].arr);
+
+ stpncpy (pc->b[2].a[1].arr, s, sizeof pc->b[2].a[1].arr);
+ sink (pc->b[2].a[1].arr);
+
+ stpncpy (pc->b[2].a[2].arr, s, sizeof pc->b[2].a[2].arr);
+ sink (pc->b[2].a[2].arr);
+}
+
+void stpncpy_str_nowarn_1 (struct C *pc, const char *s)
+{
+ stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str)[-1] = 0; /* { dg-bogus "\\\[-Wstringop-truncation" } */
+}
+
+void stpncpy_str_nowarn_2 (struct C *pc, const char *s)
+{
+ *stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str - 1) = 0; /* { dg-bogus "\\\[-Wstringop-truncation" } */
+}
+
+void stpncpy_str_nowarn_3 (struct C *pc, const char *s)
+{
+ char *d = stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+
+ d[-1] = 0;
+}
+
+void stpncpy_str_nowarn_4 (struct C *pc, const char *s)
+{
+ char *d = stpncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str - 1); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+
+ *d = 0;
+}
+
+void strncpy_arr_1 (struct C *pc, const char *s)
+{
+ strncpy (pc->b[0].a[0].arr, s, sizeof pc->b[0].a[0].arr);
+ sink (pc->b[0].a[0].arr);
+
+ strncpy (pc->b[0].a[1].arr, s, sizeof pc->b[0].a[1].arr);
+ sink (pc->b[0].a[1].arr);
+
+ strncpy (pc->b[0].a[2].arr, s, sizeof pc->b[0].a[2].arr);
+ sink (pc->b[0].a[2].arr);
+}
+
+void strncpy_str_nowarn_1 (struct C *pc, const char *s)
+{
+ strncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-bogus "\\\[-Wstringop-truncation" } */
+
+ pc->b[0].a[0].str[sizeof pc->b[0].a[0].str - 1] = 0;
+}
+
+void strncpy_str_warn_1 (struct C *pc, const char *s)
+{
+ strncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-warning "specified bound 3 equals destination size" } */
+
+ pc->b[1].a[0].str[sizeof pc->b[0].a[0].str - 1] = 0;
+}
+
+void strncpy_str_warn_2 (struct C *pc, const char *s)
+{
+ strncpy (pc->b[0].a[0].str, s, sizeof pc->b[0].a[0].str); /* { dg-warning "specified bound 3 equals destination size" } */
+
+ pc->b[0].a[1].str[sizeof pc->b[0].a[0].str - 1] = 0;
+}
CPY (d, CHOOSE ("123", "12"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 2" } */
{
- signed char n = strlen (s); /* { dg-message "length computed here" } */
+ signed char n = strlen (s); /* { dg-message "length computed here" } */
CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
}
{
- short n = strlen (s); /* { dg-message "length computed here" } */
+ short n = strlen (s); /* { dg-message "length computed here" } */
CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
}
{
- int n = strlen (s); /* { dg-message "length computed here" } */
+ int n = strlen (s); /* { dg-message "length computed here" } */
CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
}
{
- unsigned n = strlen (s); /* { dg-message "length computed here" } */
+ unsigned n = strlen (s); /* { dg-message "length computed here" } */
CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
}
{
size_t n;
- n = strlen (s); /* { dg-message "length computed here" } */
+ n = strlen (s); /* { dg-message "length computed here" } */
CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
}
{
size_t n;
char *dp2 = d + 1;
- n = strlen (s); /* { dg-message "length computed here" } */
+ n = strlen (s); /* { dg-message "length computed here" } */
CPY (dp2, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */
}
/* Exercise destination with attribute "nonstring". */
CPY (pd->c3ns, "", 3);
CPY (pd->c3ns, "", 1);
- /* Truncation is still diagnosed -- using strncpy in this case is
- pointless and should be replaced with memcpy. */
- CPY (pd->c3ns, "12", 1); /* { dg-warning "output truncated copying 1 byte from a string of length 2" } */
+ /* It could be argued that truncation in the literal case should be
+ diagnosed even for non-strings. Using strncpy in this case is
+ pointless and should be replaced with memcpy. But it would likely
+ be viewed as a false positive. */
+ CPY (pd->c3ns, "12", 1);
CPY (pd->c3ns, "12", 2);
CPY (pd->c3ns, "12", 3);
CPY (pd->c3ns, "123", 3);
strncpy (pns_1, "a", 1);
strncpy (pns_2, "ab", 2);
strncpy (pns_3, "abc", 3);
- strncpy (pns_3, s7, 3); /* { dg-warning "output truncated copying 3 bytes from a string of length 7" } */
+ strncpy (pns_3, s7, 3);
strncpy (pns_1, s, 1);
strncpy (pns_2, s, 1);
{
const char s7[] = "1234567";
+ strncpy (p->ma3, "", 0);
strncpy (p->ma3, "a", 1);
strncpy (p->ma4, "ab", 2);
strncpy (p->ma5, "abc", 3);
--- /dev/null
+/* Test to exercise warnings when an array declared with attribute "nonstring"
+ is passed to a function that expects a nul-terminated string as an argument.
+ { dg-do compile }
+ { dg-options "-O2 -Wattributes -Wstringop-overflow -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+#if __cplusplus
+extern "C" {
+#endif
+
+void* memchr (const void*, int, size_t);
+int memcmp (const void*, const void*, size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+
+int printf (const char*, ...);
+int puts (const char*);
+int puts_unlocked (const char*);
+int sprintf (char*, const char*, ...);
+int snprintf (char*, size_t, const char*, ...);
+int vsprintf (char*, const char*, va_list);
+int vsnprintf (char*, size_t, const char*, va_list);
+
+int strcmp (const char*, const char*);
+int strncmp (const char*, const char*, size_t);
+
+char* stpcpy (char*, const char*);
+char* stpncpy (char*, const char*, size_t);
+
+char* strcat (char*, const char*);
+char* strncat (char*, const char*, size_t);
+
+char* strcpy (char*, const char*);
+char* strncpy (char*, const char*, size_t);
+
+char* strchr (const char*, int);
+char* strdup (const char*);
+size_t strlen (const char*);
+size_t strnlen (const char*, size_t);
+char* strndup (const char*, size_t);
+
+#if __cplusplus
+} /* extern "C" */
+#endif
+
+#define NONSTRING __attribute__ ((nonstring))
+
+char str[4];
+char arr[4] NONSTRING;
+
+char *ptr;
+char *parr NONSTRING;
+
+struct MemArrays
+{
+ char str[4];
+ char arr[4] NONSTRING;
+ char *parr NONSTRING;
+};
+
+void sink (int, ...);
+
+
+#define T(call) sink (0, (call))
+
+void test_printf (struct MemArrays *p)
+{
+ T (printf (str));
+ T (printf (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (printf (ptr));
+ T (printf (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (printf (p->str));
+ T (printf (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_puts (struct MemArrays *p)
+{
+ T (puts (str));
+ T (puts (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (puts (ptr));
+ T (puts (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (puts (p->str));
+ T (puts (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_snprintf (char *d, size_t n, struct MemArrays *p)
+{
+ T (snprintf (d, n, str));
+ T (snprintf (d, n, arr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (snprintf (d, n, ptr));
+ T (snprintf (d, n, parr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (snprintf (d, n, p->str));
+ T (snprintf (d, n, p->arr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+}
+
+
+void test_sprintf (char *d, struct MemArrays *p)
+{
+ T (sprintf (d, str));
+ T (sprintf (d, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (sprintf (d, ptr));
+ T (sprintf (d, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (sprintf (d, p->str));
+ T (sprintf (d, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+
+void test_vsnprintf (char *d, size_t n, struct MemArrays *p, va_list va)
+{
+ T (vsnprintf (d, n, str, va));
+ T (vsnprintf (d, n, arr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (vsnprintf (d, n, ptr, va));
+ T (vsnprintf (d, n, parr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+
+ T (vsnprintf (d, n, p->str, va));
+ T (vsnprintf (d, n, p->arr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
+}
+
+
+void test_vsprintf (char *d, struct MemArrays *p, va_list va)
+{
+ T (vsprintf (d, str, va));
+ T (vsprintf (d, arr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (vsprintf (d, ptr, va));
+ T (vsprintf (d, parr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+
+ T (vsprintf (d, p->str, va));
+ T (vsprintf (d, p->arr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+
+void test_strcmp (struct MemArrays *p)
+{
+ T (strcmp (str, str));
+ T (strcmp (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcmp (arr, str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strcmp (str, ptr));
+ T (strcmp (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcmp (parr, str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strcmp (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcmp (p->arr, p->str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+ T (strcmp (p->parr, p->str)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_strncmp_warn (struct MemArrays *p)
+{
+ enum { N = sizeof arr };
+ T (strncmp (str, arr, N));
+ T (strncmp (arr, str, N));
+
+ T (strncmp (str, arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
+ T (strncmp (arr, str, N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+
+ T (strncmp (str, parr, N + 1));
+ T (strncmp (parr, str, N + 1));
+
+ T (strncmp (p->str, p->arr, N));
+ T (strncmp (p->arr, p->str, N));
+ T (strncmp (p->parr, p->str, N));
+
+ T (strncmp (p->str, p->arr, N));
+ T (strncmp (p->arr, p->str, N));
+ T (strncmp (p->parr, p->str, N));
+}
+
+
+void test_strncmp_nowarn (struct MemArrays *p, size_t n)
+{
+ T (strncmp (str, str, n));
+ T (strncmp (str, arr, n));
+ T (strncmp (arr, str, n));
+
+ T (strncmp (str, ptr, n));
+ T (strncmp (str, parr, n));
+ T (strncmp (parr, str, n));
+
+ T (strncmp (p->str, p->arr, n));
+ T (strncmp (p->arr, p->str, n));
+ T (strncmp (p->parr, p->str, n));
+}
+
+
+void test_stpcpy (struct MemArrays *p)
+{
+ T (stpcpy (str, str));
+ T (stpcpy (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (stpcpy (arr, str));
+
+ T (stpcpy (str, ptr));
+ T (stpcpy (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (stpcpy (parr, str));
+
+ T (stpcpy (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (stpcpy (p->arr, p->str));
+ T (stpcpy (p->parr, p->str));
+}
+
+
+void test_stpncpy_nowarn (struct MemArrays *p, unsigned n)
+{
+ T (stpncpy (str, str, n));
+ T (stpncpy (str, arr, n));
+ T (stpncpy (arr, str, n));
+
+ T (stpncpy (str, ptr, n));
+ T (stpncpy (str, parr, n));
+ T (stpncpy (parr, str, n));
+
+ T (stpncpy (p->str, p->arr, n));
+ T (stpncpy (p->arr, p->str, n));
+ T (stpncpy (p->parr, p->str, n));
+}
+
+
+void test_stpncpy_warn (struct MemArrays *p, unsigned n)
+{
+ enum { N = sizeof arr };
+
+ T (stpncpy (str, str, N));
+ T (stpncpy (str, arr, N));
+ T (stpncpy (arr, str, N));
+
+ T (stpncpy (str, ptr, N));
+ T (stpncpy (str, parr, N));
+ T (stpncpy (parr, str, N));
+
+ T (stpncpy (p->str, p->arr, N));
+ T (stpncpy (p->arr, p->str, N));
+ T (stpncpy (p->parr, p->str, N));
+
+ T (stpncpy (ptr, str, N + 1));
+ T (stpncpy (ptr, arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
+ T (stpncpy (arr, str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */
+
+ T (stpncpy (ptr, ptr, N + 1));
+ T (stpncpy (ptr, parr, N + 1));
+ T (stpncpy (parr, str, N + 1));
+
+ T (stpncpy (ptr, p->arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */
+ T (stpncpy (p->arr, p->str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows" } */
+ T (stpncpy (p->parr, p->str, N + 1));
+}
+
+
+void test_strcat (struct MemArrays *p)
+{
+ T (strcat (str, str));
+ T (strcat (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcat (arr, str));
+
+ T (strcat (str, ptr));
+ T (strcat (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcat (parr, str));
+
+ T (strcat (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcat (p->arr, p->str));
+ T (strcat (p->parr, p->str));
+}
+
+
+void test_strncat (struct MemArrays *p, unsigned n)
+{
+ T (strncat (str, str, n));
+ T (strncat (str, arr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strncat (arr, str, n));
+
+ T (strncat (str, ptr, n));
+ T (strncat (str, parr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strncat (parr, str, n));
+
+ T (strncat (p->str, p->arr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strncat (p->arr, p->str, n));
+ T (strncat (p->parr, p->str, n));
+}
+
+
+void test_strcpy (struct MemArrays *p)
+{
+ T (strcpy (str, str));
+ T (strcpy (str, arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcpy (arr, str));
+
+ T (strcpy (str, ptr));
+ T (strcpy (str, parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcpy (parr, str));
+
+ T (strcpy (p->str, p->arr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
+ T (strcpy (p->arr, p->str));
+ T (strcpy (p->parr, p->str));
+}
+
+
+void test_strncpy (struct MemArrays *p, unsigned n)
+{
+ T (strncpy (str, str, n));
+ T (strncpy (str, arr, n));
+ T (strncpy (arr, str, n));
+
+ T (strncpy (str, ptr, n));
+ T (strncpy (str, parr, n));
+ T (strncpy (parr, str, n));
+
+ T (strncpy (p->str, p->arr, n));
+ T (strncpy (p->arr, p->str, n));
+ T (strncpy (p->parr, p->str, n));
+}
+
+
+void test_strchr (struct MemArrays *p, int c)
+{
+ T (strchr (str, c));
+ T (strchr (arr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strchr (ptr, c));
+ T (strchr (parr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strchr (p->str, c));
+ T (strchr (p->arr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_strdup (struct MemArrays *p)
+{
+ T (strdup (str));
+ T (strdup (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strdup (ptr));
+ T (strdup (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strdup (p->str));
+ T (strdup (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+void test_stnrdup_nowarn (struct MemArrays *p, size_t n)
+{
+ T (strndup (str, n));
+ T (strndup (arr, n));
+
+ T (strndup (ptr, n));
+ T (strndup (parr, n));
+
+ T (strndup (p->str, n));
+ T (strndup (p->arr, n));
+}
+
+
+void test_stnrdup_warn (struct MemArrays *p)
+{
+ enum { N = sizeof arr };
+
+ T (strndup (str, N));
+ T (strndup (arr, N));
+
+ T (strndup (ptr, N));
+ T (strndup (parr, N));
+
+ T (strndup (p->str, N));
+ T (strndup (p->arr, N));
+
+
+ T (strndup (arr, N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+ T (strndup (parr, N + 1));
+ T (strndup (p->arr, N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+ T (strndup (p->parr, N + 1));
+}
+
+
+void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n)
+{
+ T (strlen (str));
+ T (strlen (arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strlen (ptr));
+ T (strlen (parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strlen (p->str));
+ T (strlen (p->arr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ T (strlen (s)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+ {
+ strcpy (s, "123");
+ T (strlen (s));
+ }
+
+ {
+ char a[] __attribute__ ((nonstring)) = { 1, 2, 3 };
+
+ T (strlen (a)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+ }
+
+ {
+ char a[] __attribute__ ((nonstring)) = { 1, 2, 3, 4 };
+
+ strcpy (a, "12");
+ T (strlen (a));
+ }
+
+ {
+ char *p __attribute__ ((nonstring));
+ p = (char *)__builtin_malloc (n);
+ __builtin_memset (p, '*', n);
+
+ T (strlen (p)); /* { dg-warning "argument 1 declared attribute .nonstring." } */
+
+ strcpy (p, "12345");
+ T (strlen (p));
+ }
+}
+
+
+void test_strnlen (struct MemArrays *p, size_t n)
+{
+ T (strnlen (str, n));
+ T (strnlen (arr, n));
+
+ T (strnlen (ptr, n));
+ T (strnlen (parr, n));
+
+ T (strnlen (p->str, n));
+ T (strnlen (p->arr, n));
+}
+
+
+/* Verify no warnings are issued for raw mempory functions. */
+
+void test_mem_functions (struct MemArrays *p, int c, size_t n)
+{
+ T (memchr (arr, c, n));
+ T (memchr (parr, c, n));
+ T (memchr (p->arr, c, n));
+ T (memchr (p->parr, c, n));
+
+ T (memcmp (str, arr, n));
+ T (memcmp (arr, str, n));
+ T (memcmp (str, parr, n));
+ T (memcmp (parr, str, n));
+ T (memcmp (p->str, p->arr, n));
+ T (memcmp (p->arr, p->str, n));
+ T (memcmp (p->parr, p->str, n));
+
+ T (memcpy (str, arr, n));
+ T (memcpy (arr, str, n));
+ T (memcpy (str, parr, n));
+ T (memcpy (parr, str, n));
+ T (memcpy (p->str, p->arr, n));
+ T (memcpy (p->arr, p->str, n));
+ T (memcpy (p->parr, p->str, n));
+
+ T (memmove (str, arr, n));
+ T (memmove (arr, str, n));
+ T (memmove (str, parr, n));
+ T (memmove (parr, str, n));
+ T (memmove (p->str, p->arr, n));
+ T (memmove (p->arr, p->str, n));
+ T (memmove (p->parr, p->str, n));
+}
#include "diagnostic.h"
#include "intl.h"
#include "attribs.h"
+#include "calls.h"
/* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value
is an index into strinfo vector, negative value stands for
mappings. */
static hash_map<tree_decl_hash, stridxlist> *decl_to_stridxlist_htab;
+/* Hash table mapping strlen calls to stridx instances describing
+ the calls' arguments. Non-null only when warn_stringop_truncation
+ is non-zero. */
typedef std::pair<int, location_t> stridx_strlenloc;
-static hash_map<tree, stridx_strlenloc> strlen_to_stridx;
+static hash_map<tree, stridx_strlenloc> *strlen_to_stridx;
/* Obstack for struct stridxlist and struct decl_stridxlist_map. */
static struct obstack stridx_obstack;
gcc_assert (si->full_string_p);
}
- location_t loc = gimple_location (stmt);
- strlen_to_stridx.put (lhs, stridx_strlenloc (idx, loc));
+ if (strlen_to_stridx)
+ {
+ location_t loc = gimple_location (stmt);
+ strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
+ }
return;
}
}
set_strinfo (idx, si);
find_equal_ptrs (src, idx);
- location_t loc = gimple_location (stmt);
- strlen_to_stridx.put (lhs, stridx_strlenloc (idx, loc));
+ if (strlen_to_stridx)
+ {
+ location_t loc = gimple_location (stmt);
+ strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
+ }
}
}
static bool
maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
{
- if (!warn_stringop_truncation)
- return false;
-
gimple *stmt = gsi_stmt (gsi);
wide_int cntrange[2];
return false;
tree dst = gimple_call_arg (stmt, 0);
-
- /* See if the destination is declared with attribute "nonstring"
- and if so, avoid the truncation warning. */
- if (TREE_CODE (dst) == SSA_NAME)
- {
- if (SSA_NAME_IS_DEFAULT_DEF (dst))
- dst = SSA_NAME_VAR (dst);
- else
- {
- gimple *def = SSA_NAME_DEF_STMT (dst);
-
- if (is_gimple_assign (def)
- && gimple_assign_rhs_code (def) == ADDR_EXPR)
- dst = gimple_assign_rhs1 (def);
- }
- }
-
tree dstdecl = dst;
if (TREE_CODE (dstdecl) == ADDR_EXPR)
dstdecl = TREE_OPERAND (dstdecl, 0);
- {
- tree d = dstdecl;
- if (TREE_CODE (d) == COMPONENT_REF)
- d = TREE_OPERAND (d, 1);
-
- if (DECL_P (d) && lookup_attribute ("nonstring", DECL_ATTRIBUTES (d)))
- return false;
- }
+ /* If the destination refers to a an array/pointer declared nonstring
+ return early. */
+ tree ref = NULL_TREE;
+ if (get_attr_nonstring_decl (dstdecl, &ref))
+ return false;
/* Look for dst[i] = '\0'; after the stxncpy() call and if found
avoid the truncation warning. */
if (!gsi_end_p (gsi) && is_gimple_assign (next_stmt))
{
- HOST_WIDE_INT off;
- dstdecl = get_addr_base_and_unit_offset (dstdecl, &off);
-
tree lhs = gimple_assign_lhs (next_stmt);
- tree lhsbase = get_addr_base_and_unit_offset (lhs, &off);
- if (lhsbase && operand_equal_p (dstdecl, lhsbase, 0))
+ tree_code code = TREE_CODE (lhs);
+ if (code == ARRAY_REF || code == MEM_REF)
+ lhs = TREE_OPERAND (lhs, 0);
+
+ tree func = gimple_call_fndecl (stmt);
+ if (DECL_FUNCTION_CODE (func) == BUILT_IN_STPNCPY)
+ {
+ tree ret = gimple_call_lhs (stmt);
+ if (ret && operand_equal_p (ret, lhs, 0))
+ return false;
+ }
+
+ /* Determine the base address and offset of the reference,
+ ignoring the innermost array index. */
+ if (TREE_CODE (ref) == ARRAY_REF)
+ ref = TREE_OPERAND (ref, 0);
+
+ HOST_WIDE_INT dstoff;
+ tree dstbase = get_addr_base_and_unit_offset (ref, &dstoff);
+
+ HOST_WIDE_INT lhsoff;
+ tree lhsbase = get_addr_base_and_unit_offset (lhs, &lhsoff);
+ if (lhsbase
+ && dstoff == lhsoff
+ && operand_equal_p (dstbase, lhsbase, 0))
return false;
}
static void
handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
{
+ if (!strlen_to_stridx)
+ return;
+
gimple *stmt = gsi_stmt (*gsi);
bool with_bounds = gimple_call_with_bounds_p (stmt);
/* If the length argument was computed from strlen(S) for some string
S retrieve the strinfo index for the string (PSS->FIRST) alonng with
the location of the strlen() call (PSS->SECOND). */
- stridx_strlenloc *pss = strlen_to_stridx.get (len);
+ stridx_strlenloc *pss = strlen_to_stridx->get (len);
if (!pss || pss->first <= 0)
{
if (maybe_diag_stxncpy_trunc (*gsi, src, len))
whether its value is known. Otherwise, issue the more generic
-Wstringop-overflow which triggers for LEN arguments that in
any meaningful way depend on strlen(SRC). */
- if (warn_stringop_truncation
- && sisrc == silen
- && is_strlen_related_p (src, len))
- warned = warning_at (callloc, OPT_Wstringop_truncation,
- "%qD output truncated before terminating nul "
- "copying as many bytes from a string as its length",
- func);
+ if (sisrc == silen
+ && is_strlen_related_p (src, len)
+ && warning_at (callloc, OPT_Wstringop_truncation,
+ "%qD output truncated before terminating nul "
+ "copying as many bytes from a string as its length",
+ func))
+ warned = true;
else if (silen && is_strlen_related_p (src, silen->ptr))
warned = warning_at (callloc, OPT_Wstringop_overflow_,
"%qD specified bound depends on the length "
fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
gimple_assign_rhs2 (stmt), stmt);
- tree rhs1 = gimple_assign_rhs1 (stmt);
- if (stridx_strlenloc *ps = strlen_to_stridx.get (rhs1))
- strlen_to_stridx.put (lhs, stridx_strlenloc (*ps));
+ if (strlen_to_stridx)
+ {
+ tree rhs1 = gimple_assign_rhs1 (stmt);
+ if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1))
+ strlen_to_stridx->put (lhs, stridx_strlenloc (*ps));
+ }
}
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
{
unsigned int
pass_strlen::execute (function *fun)
{
+ gcc_assert (!strlen_to_stridx);
+ if (warn_stringop_overflow || warn_stringop_truncation)
+ strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
+
ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names);
max_stridx = 1;
laststmt.len = NULL_TREE;
laststmt.stridx = 0;
- strlen_to_stridx.empty ();
+ if (strlen_to_stridx)
+ {
+ strlen_to_stridx->empty ();
+ delete strlen_to_stridx;
+ strlen_to_stridx = NULL;
+ }
return 0;
}