Hi Dave, thanks for your review. I have corrected the issues you
pointed out and sent an updated patch. What follows are my comments on
some of the issues.
[...snip...]
+void
+kf_mktemp_family::check_template_with_suffixlen_arg (
+ const call_details &cd, unsigned int suffixlen_arg_idx)
+{
+ const svalue *suffixlen_sval = cd.get_arg_svalue (suffixlen_arg_idx);
+ const constant_svalue *cst_sval
+ = suffixlen_sval->dyn_cast_constant_svalue ();
+ if (!cst_sval)
+ {
+ /* Suffix length unknown, but we still need to check whether the
template
+ argument is a null-terminated string. */
+ region_model *model = cd.get_model ();
+ model->check_for_null_terminated_string_arg (cd, 0, false, nullptr);
+ return;
+ }
+
+ tree cst = cst_sval->get_constant ();
+ /* TODO: Negative suffixlen is always wrong and potentially OOB,
maybe add a
+ warning in the future? */
+ if (tree_int_cst_sgn (cst) < 0)
+ return;
I wonder if this would reliably generate EINVAL on all implementations?
Let's leave any warning about this to a followup.
Not on all of them. Some like Gnulib do generate EINVAL. But others
don't; for example, in `libiberty/mkstemps.c` , `mkstemps` doesn't check
whether suffix_len is negative:
if ((int) len < 6 + suffix_len
|| strncmp (&pattern[len - 6 - suffix_len], "XXXXXX", 6))
{
return -1;
}
There could be more cases like libiberty's, so that's why I think it
could be a good idea to add a warning. Maybe it could be made more
general so it can be used in similar cases, e.g. `ftruncate(int fd,
off_t length)` when passed a negative length.
+void
+kf_mktemp_family::check_template (const call_details &cd, size_t
trailing_len)
+{
+ region_model_context *ctxt = cd.get_ctxt ();
+ gcc_assert (ctxt);
+ const svalue *ptr_sval = cd.get_arg_svalue (0);
+ region_model *model = cd.get_model ();
+
+ const svalue *strlen_sval
+ = model->check_for_null_terminated_string_arg (cd, 0, false, nullptr);
+ if (!strlen_sval)
+ return;
+
+ if (cd.get_arg_string_literal (0))
+ {
+ ctxt->warn (std::make_unique<mktemp_of_string_literal> (cd));
+ }
+ else if (check_placeholder (cd, trailing_len, ptr_sval, strlen_sval)
+ .is_false ())
+ {
+ ctxt->warn (
+ std::make_unique<mktemp_missing_placeholder> (cd, trailing_len));
+ }
Note that (for better or worse) our coding standard is to omit
redundant { } blocks, so the above can be written:
if (cd.get_arg_string_literal (0))
ctxt->warn (std::make_unique<mktemp_of_string_literal> (cd));
else if (check_placeholder (cd, trailing_len, ptr_sval, strlen_sval)
.is_false ())
ctxt->warn (std::make_unique<mktemp_missing_placeholder>
(cd, trailing_len));
which might save a little horizontal space.
I was aware that the standard calls for omitting { } when not strictly
needed, but I didn't know if the block could be added in some cases
where it may benefit readability (e.g. see the fragment from libiberty
above). Given that now I know it's a strict rule, I will remove the
brackets.
+}
+
+void
+kf_mktemp_family::check_flags (const call_details &cd,
+ unsigned int flags_arg_idx)
+{
+ region_model_context *ctxt = cd.get_ctxt ();
+ gcc_assert (ctxt);
+
+ const svalue *flags_sval = cd.get_arg_svalue (flags_arg_idx);
+ const constant_svalue *cst = flags_sval->dyn_cast_constant_svalue ();
+ if (!cst)
+ return;
- int mkstemp(char *template);
+ unsigned HOST_WIDE_INT flags = TREE_INT_CST_LOW (cst->get_constant ());
+
+ /* Check whether any of the implicit flags are redundantly specified. */
+ unsigned HOST_WIDE_INT implicit_flags = 0;
+ for (const char *name : { "O_RDWR", "O_CREAT", "O_EXCL" })
+ if (tree cst_tree = get_stashed_constant_by_name (name))
+ implicit_flags |= TREE_INT_CST_LOW (cst_tree);
Note that get_stashed_constant_by_name can fail and return null
(see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108708), but the way
you've written this, it's OK; the pertinent bits of implicit_flags
won't get set and so the warning won't fire.
My intention was for the `if` to guard from that case, as the result of
the expression containing the assignment would be evaluated to null.
Isn't that the case? It's true that the `if` could be omitted, but I
think skipping it when `get_stashed_constant_by_name` returns null is
more explicit.
[...snip...]
diff --git a/gcc/testsuite/gcc.dg/analyzer/mkostemp-1.c
[...snip...]
+void test_redundant_o_rdwr (void)
+{
+ char tmpl[] = "/tmp/testXXXXXX";
+ mkostemp (tmpl, O_RDWR); /* { dg-warning "'mkostemp' flags argument
should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these are already
implied" } */
+}
Note that getting a stashed constant can currently fail depending on
the system headers; see e.g.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108708
So we might want to add some kind of dg-skip on some systems (difficult
to say ahead of time though).
I added a couple of comments pointing that out for future reference. Is
there some kind of testing in multiple platforms already in place, or
are these issues uncovered when individual people test it on their
machine and see it fail?
[...snip...]
diff --git a/gcc/testsuite/gcc.dg/analyzer/mktemp-1.c
b/gcc/testsuite/gcc.dg/analyzer/mktemp-1.c
new file mode 100644
index 00000000000..19b565c5321
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/mktemp-1.c
@@ -0,0 +1,99 @@
+/* { dg-additional-options "-Wno-analyzer-null-argument" } */
+
+/* TODO: mktemp is deprecated per MSC24-C
+ (https://wiki.sei.cmu.edu/confluence/x/hNYxBQ).
+ Once a warning for deprecated functions exists, mktemp should
+ also warn about its use. */
I was thinking that we could just assume that the system header should
have:
__attribute__ ((deprecated)) on the decl of mktemp; see
https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-deprecated
but it looks like mktemp is sufficiently unsafe that we should probably
issue some kind of "use of inherently unsafe function" warning.
I've been thinking about this. MSC24-C lists both deprecated and
obsolescent functions. I think it could be reasonable to assume that
deprecated functions like `mktemp` have `__attribute__ ((deprecated))`,
as they have been removed from the standards. If someone is using a
library that doesn't mark it as deprecated, maybe it's reasonable to
assume it's likely an old library that doesn't have an alternative.
What do you think?
On the other hand, obsolescent functions are defined as such by MSC24-C,
and it's an opinionated list. I think it would make sense to add a
warning for MSC24-C, but it probably should be disabled by default.
What I'm not sure is how to handle MSC24-C warnings for deprecated
functions in that case: the attribute warning will already catch them if
present, but a user may want to know that MSC24-C also recommends not
using them, or may want to be warned about them when enabling MSC24-C
even in a codebase with old system headers.
Let's leave that to a followup.
Agreed.
Thanks again for the patch; hope the above makes sense.
Thanks for your review! It's very useful.
Tomás