The patch "analyzer: new warnings -Wanalyzer-mkstemp-missing-suffix
and -Wanalyzer-mkstemp-of-string-literal [PR105890]" added those two
warnings for mkstemp only. This patch generalizes them to the whole
mktemp family (including GNU extensions): mktemp, mkstemp, mkostemp,
mkstemps, mkostemps, and mkdtemp.
The two warnings are renamed to reflect their broader scope:
-Wanalyzer-mkstemp-missing-suffix becomes
-Wanalyzer-mktemp-missing-placeholder. For the suffixed variants
(mkstemps, mkostemps), the diagnostic accounts for the suffix length
when locating the "XXXXXX" placeholder.
-Wanalyzer-mkstemp-of-string-literal becomes
-Wanalyzer-mktemp-of-string-literal.
A new warning is also added:
-Wanalyzer-mkostemp-redundant-flags warns when mkostemp or
mkostemps is called with flags that include O_RDWR, O_CREAT, or
O_EXCL, which are already implied by these functions and produce
errors on some systems.
All three warnings are enabled by default under -fanalyzer.
Bootstrapped and tested on x86_64-pc-linux-gnu.
gcc/analyzer/ChangeLog:
PR analyzer/105890
* analyzer-language.cc (stash_named_constants): Stash O_CREAT,
O_EXCL, and O_RDWR for use by kf.cc.
* analyzer.opt: Rename -Wanalyzer-mkstemp-missing-suffix to
-Wanalyzer-mktemp-missing-placeholder and
-Wanalyzer-mkstemp-of-string-literal to
-Wanalyzer-mktemp-of-string-literal. Add
-Wanalyzer-mkostemp-redundant-flags. Fix alphabetical ordering.
* analyzer.opt.urls: Regenerate.
* kf.cc (class mkstemp_of_string_literal): Rename to...
(class mktemp_of_string_literal): ...this.
(class mkstemp_missing_suffix): Rename to...
(class mktemp_missing_placeholder): ...this. Add trailing_len
parameter for suffixed variants.
(class mkostemp_redundant_flags): New diagnostic class.
(class kf_mktemp_family): New base class with shared template
and flags checking logic.
(kf_mktemp_family::check_template_with_suffixlen_arg): New.
(kf_mktemp_family::check_template): New.
(kf_mktemp_family::check_flags): New.
(kf_mktemp_family::check_placeholder): New.
(class kf_mkstemp): Rename to...
(class kf_mktemp_simple): ...this. Generalize to handle mktemp,
mkstemp, and mkdtemp.
(class kf_mkostemp): New known_function handler.
(class kf_mkostemps): New known_function handler.
(class kf_mkstemps): New known_function handler.
(register_known_functions): Register all mktemp family handlers.
gcc/ChangeLog:
PR analyzer/105890
* doc/invoke.texi: Rename -Wanalyzer-mkstemp-missing-suffix to
-Wanalyzer-mktemp-missing-placeholder and
-Wanalyzer-mkstemp-of-string-literal to
-Wanalyzer-mktemp-of-string-literal. Add
-Wanalyzer-mkostemp-redundant-flags. Fix alphabetical ordering
of detailed descriptions.
gcc/testsuite/ChangeLog:
PR analyzer/105890
* gcc.dg/analyzer/mkstemp-1.c: Update terminology from "suffix"
to "placeholder".
* gcc.dg/analyzer/mkdtemp-1.c: New test.
* gcc.dg/analyzer/mkostemp-1.c: New test.
* gcc.dg/analyzer/mkostemps-1.c: New test.
* gcc.dg/analyzer/mkstemps-1.c: New test.
* gcc.dg/analyzer/mktemp-1.c: New test.
Signed-off-by: Tomas Ortin Fernandez (quanrong) <[email protected]>
---
gcc/analyzer/analyzer-language.cc | 5 +
gcc/analyzer/analyzer.opt | 20 +-
gcc/analyzer/analyzer.opt.urls | 15 +-
gcc/analyzer/kf.cc | 416 ++++++++++++++++----
gcc/doc/invoke.texi | 69 ++--
gcc/testsuite/gcc.dg/analyzer/mkdtemp-1.c | 87 ++++
gcc/testsuite/gcc.dg/analyzer/mkostemp-1.c | 125 ++++++
gcc/testsuite/gcc.dg/analyzer/mkostemps-1.c | 126 ++++++
gcc/testsuite/gcc.dg/analyzer/mkstemp-1.c | 14 +-
gcc/testsuite/gcc.dg/analyzer/mkstemps-1.c | 88 +++++
gcc/testsuite/gcc.dg/analyzer/mktemp-1.c | 99 +++++
11 files changed, 936 insertions(+), 128 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/analyzer/mkdtemp-1.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/mkostemp-1.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/mkostemps-1.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/mkstemps-1.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/mktemp-1.c
diff --git a/gcc/analyzer/analyzer-language.cc
b/gcc/analyzer/analyzer-language.cc
index aa8125e0654..99a829aa759 100644
--- a/gcc/analyzer/analyzer-language.cc
+++ b/gcc/analyzer/analyzer-language.cc
@@ -77,6 +77,11 @@ stash_named_constants (logger *logger, const
translation_unit &tu)
maybe_stash_named_constant (logger, tu, "O_WRONLY");
maybe_stash_named_constant (logger, tu, "SOCK_STREAM");
maybe_stash_named_constant (logger, tu, "SOCK_DGRAM");
+
+ /* Stash named constants for use by kf.cc */
+ maybe_stash_named_constant (logger, tu, "O_CREAT");
+ maybe_stash_named_constant (logger, tu, "O_EXCL");
+ maybe_stash_named_constant (logger, tu, "O_RDWR");
}
/* Hook for frontend to call into analyzer when TU finishes.
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 8b683b4cb6a..389f4ea946a 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -154,6 +154,18 @@ Wanalyzer-mismatching-deallocation
Common Var(warn_analyzer_mismatching_deallocation) Init(1) Warning
Warn about code paths in which the wrong deallocation function is called.
+Wanalyzer-mkostemp-redundant-flags
+Common Var(warn_analyzer_mkostemp_redundant_flags) Init(1) Warning
+Warn about code paths in which mkostemp or mkostemps are called with
flags that are already included internally.
+
+Wanalyzer-mktemp-missing-placeholder
+Common Var(warn_analyzer_mktemp_missing_placeholder) Init(1) Warning
+Warn about code paths in which a function in the mktemp family is
called with a template not containing the placeholder \"XXXXXX\".
+
+Wanalyzer-mktemp-of-string-literal
+Common Var(warn_analyzer_mktemp_of_string_literal) Init(1) Warning
+Warn about code paths in which a string literal is passed to a function
in the mktemp family.
+
Wanalyzer-out-of-bounds
Common Var(warn_analyzer_out_of_bounds) Init(1) Warning
Warn about code paths in which a write or read to a buffer is
out-of-bounds.
@@ -182,14 +194,6 @@ Wanalyzer-null-dereference
Common Var(warn_analyzer_null_dereference) Init(1) Warning
Warn about code paths in which a NULL pointer is dereferenced.
-Wanalyzer-mkstemp-missing-suffix
-Common Var(warn_analyzer_mkstemp_missing_suffix) Init(1) Warning
-Warn about code paths in which mkstemp is called with a template not
ending in \"XXXXXX\".
-
-Wanalyzer-mkstemp-of-string-literal
-Common Var(warn_analyzer_mkstemp_of_string_literal) Init(1) Warning
-Warn about code paths in which a string literal is passed to mkstemp.
-
Wanalyzer-putenv-of-auto-var
Common Var(warn_analyzer_putenv_of_auto_var) Init(1) Warning
Warn about code paths in which an on-stack buffer is passed to putenv.
diff --git a/gcc/analyzer/analyzer.opt.urls b/gcc/analyzer/analyzer.opt.urls
index d98174a00d6..db56a7209e9 100644
--- a/gcc/analyzer/analyzer.opt.urls
+++ b/gcc/analyzer/analyzer.opt.urls
@@ -63,6 +63,15 @@
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-malloc-leak)
Wanalyzer-mismatching-deallocation
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-mismatching-deallocation)
+Wanalyzer-mkostemp-redundant-flags
+UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-mkostemp-redundant-flags)
+
+Wanalyzer-mktemp-missing-placeholder
+UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-mktemp-missing-placeholder)
+
+Wanalyzer-mktemp-of-string-literal
+UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-mktemp-of-string-literal)
+
Wanalyzer-out-of-bounds
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-out-of-bounds)
@@ -84,12 +93,6 @@
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-null-argument)
Wanalyzer-null-dereference
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-null-dereference)
-Wanalyzer-mkstemp-missing-suffix
-UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-mkstemp-missing-suffix)
-
-Wanalyzer-mkstemp-of-string-literal
-UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-mkstemp-of-string-literal)
-
Wanalyzer-putenv-of-auto-var
UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-putenv-of-auto-var)
diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc
index 6ecdf5c4006..73d5966dfcd 100644
--- a/gcc/analyzer/kf.cc
+++ b/gcc/analyzer/kf.cc
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
+#include "tree.h"
#if ENABLE_ANALYZER
@@ -747,21 +748,21 @@ kf_memset::impl_call_pre (const call_details &cd)
const
cd.maybe_set_lhs (dest_sval);
}
-/* A subclass of pending_diagnostic for complaining about 'mkstemp'
- called on a string literal. */
+/* A subclass of pending_diagnostic for complaining about functions on the
+ 'mktemp' family called on a string literal. */
-class mkstemp_of_string_literal : public undefined_function_behavior
+class mktemp_of_string_literal : public undefined_function_behavior
{
public:
- mkstemp_of_string_literal (const call_details &cd)
- : undefined_function_behavior (cd)
+ mktemp_of_string_literal (const call_details &cd)
+ : undefined_function_behavior (cd)
{
}
int
get_controlling_option () const final override
{
- return OPT_Wanalyzer_mkstemp_of_string_literal;
+ return OPT_Wanalyzer_mktemp_of_string_literal;
}
bool
@@ -792,15 +793,82 @@ public:
}
};
-/* A subclass of pending_diagnostic for complaining about 'mkstemp'
- called with a template that does not end with "XXXXXX". */
+/* A subclass of pending_diagnostic for complaining about functions in the
+ 'mktemp' family called with a template that does not contain the
expected
+ "XXXXXX" placeholder. */
+
+class mktemp_missing_placeholder
+ : public pending_diagnostic_subclass<mktemp_missing_placeholder>
+{
+public:
+ mktemp_missing_placeholder (const call_details &cd, size_t trailing_len)
+ : m_call_stmt (cd.get_call_stmt ()), m_fndecl
(cd.get_fndecl_for_call ()),
+ m_trailing_len (trailing_len)
+ {
+ gcc_assert (m_fndecl);
+ }
+
+ const char *
+ get_kind () const final override
+ {
+ return "mktemp_missing_placeholder";
+ }
+
+ bool
+ operator== (const mktemp_missing_placeholder &other) const
+ {
+ return &m_call_stmt == &other.m_call_stmt;
+ }
+
+ int
+ get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_mktemp_missing_placeholder;
+ }
+
+ bool
+ emit (diagnostic_emission_context &ctxt) final override
+ {
+ if (m_trailing_len == 0)
+ return ctxt.warn ("%qE template string does not end with %qs",
m_fndecl,
+ "XXXXXX");
+ else
+ return ctxt.warn ("%qE template string does not contain %qs"
+ " before a %zu-character suffix",
+ m_fndecl, "XXXXXX", m_trailing_len);
+ }
+
+ bool
+ describe_final_event (pretty_printer &pp,
+ const evdesc::final_event &) final override
+ {
+ if (m_trailing_len == 0)
+ pp_printf (&pp, "%qE template string does not end with %qs",
m_fndecl,
+ "XXXXXX");
+ else
+ pp_printf (&pp,
+ "%qE template string does not contain %qs"
+ " before a %zu-character suffix",
+ m_fndecl, "XXXXXX", m_trailing_len);
+ return true;
+ }
+
+private:
+ const gimple &m_call_stmt;
+ tree m_fndecl; // non-NULL
+ size_t m_trailing_len;
+};
+
+/* A subclass of pending_diagnostic for complaining about 'mkostemp'
+ or 'mkostemps' called with flags that are already included
+ internally (O_CREAT, O_EXCL, O_RDWR). */
-class mkstemp_missing_suffix
- : public pending_diagnostic_subclass<mkstemp_missing_suffix>
+class mkostemp_redundant_flags
+ : public pending_diagnostic_subclass<mkostemp_redundant_flags>
{
public:
- mkstemp_missing_suffix (const call_details &cd)
- : m_call_stmt (cd.get_call_stmt ()), m_fndecl
(cd.get_fndecl_for_call ())
+ mkostemp_redundant_flags (const call_details &cd)
+ : m_call_stmt (cd.get_call_stmt ()), m_fndecl
(cd.get_fndecl_for_call ())
{
gcc_assert (m_fndecl);
}
@@ -808,11 +876,11 @@ public:
const char *
get_kind () const final override
{
- return "mkstemp_missing_suffix";
+ return "mkostemp_redundant_flags";
}
bool
- operator== (const mkstemp_missing_suffix &other) const
+ operator== (const mkostemp_redundant_flags &other) const
{
return &m_call_stmt == &other.m_call_stmt;
}
@@ -820,22 +888,26 @@ public:
int
get_controlling_option () const final override
{
- return OPT_Wanalyzer_mkstemp_missing_suffix;
+ return OPT_Wanalyzer_mkostemp_redundant_flags;
}
bool
emit (diagnostic_emission_context &ctxt) final override
{
- return ctxt.warn ("%qE template string does not end with %qs",
m_fndecl,
- "XXXXXX");
+ return ctxt.warn (
+ "%qE flags argument should not include %<O_RDWR%>, %<O_CREAT%>,"
+ " or %<O_EXCL%> as these are already implied",
+ m_fndecl);
}
bool
describe_final_event (pretty_printer &pp,
const evdesc::final_event &) final override
{
- pp_printf (&pp, "%qE template string does not end with %qs", m_fndecl,
- "XXXXXX");
+ pp_printf (&pp,
+ "%qE flags argument should not include %<O_RDWR%>, %<O_CREAT%>,"
+ " or %<O_EXCL%> as these are already implied",
+ m_fndecl);
return true;
}
@@ -844,14 +916,177 @@ private:
tree m_fndecl; // non-NULL
};
-/* Handler for calls to "mkstemp":
+class kf_mktemp_family : public known_function
+{
+protected:
+ /* Extract the suffixlen from the call argument at SUFFIXLEN_ARG_IDX
+ and check the template. If the suffixlen is not a compile-time
+ constant, the check is skipped. */
+ static void
+ check_template_with_suffixlen_arg (const call_details &cd,
+ unsigned int suffixlen_arg_idx);
+
+ /* Check that the template argument (arg 0) is not a string literal
+ and contains the "XXXXXX" placeholder at the expected position.
+ TRAILING_LEN is the number of characters after the placeholder
+ (0 for mkstemp/mkdtemp/mktemp/mkostemp, or the suffixlen value
+ for mkstemps/mkostemps). */
+ static void check_template (const call_details &cd, size_t trailing_len);
+
+ /* Check whether the flags argument at FLAGS_ARG_IDX contains any of
+ O_RDWR, O_CREAT, or O_EXCL, which are already included internally
+ by mkostemp/mkostemps. */
+ static void check_flags (const call_details &cd, unsigned int
flags_arg_idx);
+
+private:
+ static const int PLACEHOLDER_LEN = 6;
+
+ /* Return true if the placeholder is "XXXXXX", false if it definitely
isn't,
+ or unknown if we can't determine. */
+ static tristate check_placeholder (const call_details &cd,
+ size_t trailing_len,
+ const svalue *ptr_sval,
+ const svalue *strlen_sval);
+};
+
+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;
+
+ unsigned HOST_WIDE_INT suffixlen = TREE_INT_CST_LOW (cst);
+ check_template (cd, suffixlen);
+}
+
+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));
+ }
+}
+
+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);
+
+ if (flags & implicit_flags)
+ ctxt->warn (std::make_unique<mkostemp_redundant_flags> (cd));
+}
+
+tristate
+kf_mktemp_family::check_placeholder (const call_details &cd,
+ size_t trailing_len,
+ const svalue *ptr_sval,
+ const svalue *strlen_sval)
+{
+ region_model *model = cd.get_model ();
+
+ const constant_svalue *len_cst =
strlen_sval->dyn_cast_constant_svalue ();
+ if (!len_cst)
+ return tristate::TS_UNKNOWN;
+
+ byte_offset_t len = TREE_INT_CST_LOW (len_cst->get_constant ());
+ if (len < PLACEHOLDER_LEN + trailing_len)
+ return tristate::TS_FALSE;
+
+ tree arg_tree = cd.get_arg_tree (0);
+ const region *reg = model->deref_rvalue (ptr_sval, arg_tree,
cd.get_ctxt ());
+
+ /* Find the byte offset of the pointed-to region. */
+ region_offset reg_offset = reg->get_offset (cd.get_manager ());
+ if (reg_offset.symbolic_p ())
+ return tristate::TS_UNKNOWN;
+ byte_offset_t ptr_byte_offset;
+ if (!reg_offset.get_concrete_byte_offset (&ptr_byte_offset))
+ return tristate::TS_UNKNOWN;
+
+ const region *base_reg = reg->get_base_region ();
+ const svalue *base_sval = model->get_store_value (base_reg,
cd.get_ctxt ());
+
+ const constant_svalue *cst_sval = base_sval->dyn_cast_constant_svalue ();
+ if (!cst_sval)
+ return tristate::TS_UNKNOWN;
+
+ tree cst = cst_sval->get_constant ();
+ if (TREE_CODE (cst) != STRING_CST)
+ return tristate::TS_UNKNOWN;
+
+ HOST_WIDE_INT str_len = len.to_shwi ();
+ HOST_WIDE_INT start = ptr_byte_offset.to_shwi ();
+
+ /* Ensure we can read up to and including the NUL terminator at position
+ [start + str_len - trailing_len] within the STRING_CST. */
+ HOST_WIDE_INT range = start + str_len - trailing_len + 1;
+ if (range > TREE_STRING_LENGTH (cst))
+ return tristate::TS_UNKNOWN;
+
+ if (memcmp (TREE_STRING_POINTER (cst) + start + str_len - trailing_len
+ - PLACEHOLDER_LEN,
+ "XXXXXX", PLACEHOLDER_LEN)
+ != 0)
+ return tristate::TS_FALSE;
+
+ return tristate::TS_TRUE;
+}
+
+/* Handler for calls to "mkdtemp", "mkstemp", and "mktemp", which all
+ take a single char * template argument.
The template must not be a string constant, and its last six
characters must be "XXXXXX". */
-class kf_mkstemp : public known_function
+class kf_mktemp_simple : public kf_mktemp_family
{
public:
bool
@@ -864,86 +1099,96 @@ public:
impl_call_pre (const call_details &cd) const final override
{
if (cd.get_ctxt ())
- check_template (cd);
+ check_template (cd, 0);
cd.set_any_lhs_with_defaults ();
}
+};
-private:
- static void
- check_template (const call_details &cd)
- {
- region_model_context *ctxt = cd.get_ctxt ();
- const svalue *ptr_sval = cd.get_arg_svalue (0);
- region_model *model = cd.get_model ();
+/* Handler for calls to "mkostemp":
- const svalue *strlen_sval
- = model->check_for_null_terminated_string_arg (cd, 0, false,
nullptr);
- if (!strlen_sval)
- return;
+ int mkostemp(char *template, int flags);
- if (cd.get_arg_string_literal (0))
- {
- ctxt->warn (std::make_unique<mkstemp_of_string_literal> (cd));
- }
- else if (check_suffix (cd, ptr_sval, strlen_sval).is_false ())
+ The template must not be a string constant, and its last six
+ characters must be "XXXXXX". Warns when flags contains O_RDWR,
+ O_CREAT, or O_EXCL, which are already included internally. */
+
+class kf_mkostemp : public kf_mktemp_family
+{
+public:
+ bool
+ matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
+ }
+
+ void
+ impl_call_pre (const call_details &cd) const final override
+ {
+ if (cd.get_ctxt ())
{
- ctxt->warn (std::make_unique<mkstemp_missing_suffix> (cd));
+ check_template (cd, 0);
+ check_flags (cd, 1);
}
+
+ cd.set_any_lhs_with_defaults ();
}
+};
- /* Return true if the template ends with "XXXXXX", false if it
- definitely does not, or unknown if we can't determine. */
- static tristate
- check_suffix (const call_details &cd, const svalue *ptr_sval,
- const svalue *strlen_sval)
- {
- region_model *model = cd.get_model ();
+/* Handler for calls to "mkostemps":
- const constant_svalue *len_cst =
strlen_sval->dyn_cast_constant_svalue ();
- if (!len_cst)
- return tristate::TS_UNKNOWN;
+ int mkostemps(char *template, int suffixlen, int flags);
- byte_offset_t len = TREE_INT_CST_LOW (len_cst->get_constant ());
- if (len < 6)
- return tristate::TS_FALSE;
+ The template must not be a string constant, and must contain
+ "XXXXXX" before a suffixlen-character suffix. Warns when flags
+ contains O_RDWR, O_CREAT, or O_EXCL, which are already included
+ internally. */
- tree arg_tree = cd.get_arg_tree (0);
- const region *reg
- = model->deref_rvalue (ptr_sval, arg_tree, cd.get_ctxt ());
+class kf_mkostemps : public kf_mktemp_family
+{
+public:
+ bool
+ matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 3 && cd.arg_is_pointer_p (0));
+ }
- /* Find the byte offset of the pointed-to region. */
- region_offset reg_offset = reg->get_offset (cd.get_manager ());
- if (reg_offset.symbolic_p ())
- return tristate::TS_UNKNOWN;
- byte_offset_t ptr_byte_offset;
- if (!reg_offset.get_concrete_byte_offset (&ptr_byte_offset))
- return tristate::TS_UNKNOWN;
+ void
+ impl_call_pre (const call_details &cd) const final override
+ {
+ if (cd.get_ctxt ())
+ {
+ check_template_with_suffixlen_arg (cd, 1);
+ check_flags (cd, 2);
+ }
- const region *base_reg = reg->get_base_region ();
- const svalue *base_sval
- = model->get_store_value (base_reg, cd.get_ctxt ());
+ cd.set_any_lhs_with_defaults ();
+ }
+};
- const constant_svalue *cst_sval =
base_sval->dyn_cast_constant_svalue ();
- if (!cst_sval)
- return tristate::TS_UNKNOWN;
+/* Handler for calls to "mkstemps":
- tree cst = cst_sval->get_constant ();
- if (TREE_CODE (cst) != STRING_CST)
- return tristate::TS_UNKNOWN;
+ int mkstemps(char *template, int suffixlen);
- HOST_WIDE_INT str_len = len.to_shwi ();
- HOST_WIDE_INT start = ptr_byte_offset.to_shwi ();
+ The template must not be a string constant, and must contain
+ "XXXXXX" before a suffixlen-character suffix. */
- /* Ensure the range [start, start + str_len] fits. */
- if (1 + start + str_len > TREE_STRING_LENGTH (cst))
- return tristate::TS_UNKNOWN;
+class kf_mkstemps : public kf_mktemp_family
+{
+public:
+ bool
+ matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
+ }
- if (memcmp (TREE_STRING_POINTER (cst) + start + str_len - 6,
"XXXXXX", 6)
- != 0)
- return tristate::TS_FALSE;
+ void
+ impl_call_pre (const call_details &cd) const final override
+ {
+ if (cd.get_ctxt ())
+ check_template_with_suffixlen_arg (cd, 1);
- return tristate::TS_TRUE;
+ cd.set_any_lhs_with_defaults ();
}
};
@@ -2546,7 +2791,14 @@ register_known_functions (known_function_manager
&kfm,
/* Known POSIX functions, and some non-standard extensions. */
{
kfm.add ("fopen", std::make_unique<kf_fopen> ());
- kfm.add ("mkstemp", std::make_unique<kf_mkstemp> ());
+ kfm.add ("mkdtemp", std::make_unique<kf_mktemp_simple> ());
+ kfm.add ("mkostemp", std::make_unique<kf_mkostemp> ());
+ kfm.add ("mkostemps", std::make_unique<kf_mkostemps> ());
+ kfm.add ("mkstemps", std::make_unique<kf_mkstemps> ());
+ kfm.add ("mkstemp", std::make_unique<kf_mktemp_simple> ());
+ /* TODO: Report mktemp as deprecated per MSC24-C
+ (https://wiki.sei.cmu.edu/confluence/x/hNYxBQ). */
+ kfm.add ("mktemp", std::make_unique<kf_mktemp_simple> ());
kfm.add ("putenv", std::make_unique<kf_putenv> ());
kfm.add ("strtok", std::make_unique<kf_strtok> (rmm));
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7fac910dd0f..f23fd6e925b 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -527,8 +527,9 @@ Objective-C and Objective-C++ Dialects}.
-Wno-analyzer-jump-through-null
-Wno-analyzer-malloc-leak
-Wno-analyzer-mismatching-deallocation
--Wno-analyzer-mkstemp-of-string-literal
--Wno-analyzer-mkstemp-missing-suffix
+-Wno-analyzer-mkostemp-redundant-flags
+-Wno-analyzer-mktemp-missing-placeholder
+-Wno-analyzer-mktemp-of-string-literal
-Wno-analyzer-null-argument
-Wno-analyzer-null-dereference
-Wno-analyzer-out-of-bounds
@@ -11561,8 +11562,9 @@ Enabling this option effectively enables the
following warnings:
-Wanalyzer-jump-through-null
-Wanalyzer-malloc-leak
-Wanalyzer-mismatching-deallocation
--Wanalyzer-mkstemp-of-string-literal
--Wanalyzer-mkstemp-missing-suffix
+-Wanalyzer-mkostemp-redundant-flags
+-Wanalyzer-mktemp-missing-placeholder
+-Wanalyzer-mktemp-of-string-literal
-Wanalyzer-null-argument
-Wanalyzer-null-dereference
-Wanalyzer-out-of-bounds
@@ -11939,27 +11941,6 @@ or a function marked with attribute @code{malloc}.
See @uref{https://cwe.mitre.org/data/definitions/401.html, CWE-401:
Missing Release of Memory after Effective Lifetime}.
-@opindex Wanalyzer-mkstemp-missing-suffix
-@opindex Wno-analyzer-mkstemp-missing-suffix
-@item -Wno-analyzer-mkstemp-missing-suffix
-This warning requires @option{-fanalyzer}, which enables it; use
-@option{-Wno-analyzer-mkstemp-missing-suffix} to disable it.
-
-This diagnostic warns for paths through the code in which the template
-string passed to @code{mkstemp} does not end with @samp{XXXXXX}.
-
-@opindex Wanalyzer-mkstemp-of-string-literal
-@opindex Wno-analyzer-mkstemp-of-string-literal
-@item -Wno-analyzer-mkstemp-of-string-literal
-This warning requires @option{-fanalyzer}, which enables it; use
-@option{-Wno-analyzer-mkstemp-of-string-literal} to disable it.
-
-This diagnostic warns for paths through the code in which @code{mkstemp}
-is called on a string literal. Since @code{mkstemp} modifies its
-argument in place, passing a string literal leads to undefined behavior.
-
-See @uref{https://wiki.sei.cmu.edu/confluence/x/VtYxBQ, STR30-C. Do not
attempt to modify string literals}.
-
@opindex Wanalyzer-mismatching-deallocation
@opindex Wno-analyzer-mismatching-deallocation
@item -Wno-analyzer-mismatching-deallocation
@@ -11976,6 +11957,44 @@ pairs using attribute @code{malloc}.
See @uref{https://cwe.mitre.org/data/definitions/762.html, CWE-762:
Mismatched Memory Management Routines}.
+@opindex Wanalyzer-mkostemp-redundant-flags
+@opindex Wno-analyzer-mkostemp-redundant-flags
+@item -Wno-analyzer-mkostemp-redundant-flags
+This warning requires @option{-fanalyzer}, which enables it; use
+@option{-Wno-analyzer-mkostemp-redundant-flags} to disable it.
+
+This diagnostic warns for paths through the code in which
+@code{mkostemp} or @code{mkostemps} is called with flags that include
+@code{O_RDWR}, @code{O_CREAT}, or @code{O_EXCL}. These flags are
+already implied by the function and specifying them is unnecessary, and
+produces errors on some systems.
+
+@opindex Wanalyzer-mktemp-missing-placeholder
+@opindex Wno-analyzer-mktemp-missing-placeholder
+@item -Wno-analyzer-mktemp-missing-placeholder
+This warning requires @option{-fanalyzer}, which enables it; use
+@option{-Wno-analyzer-mktemp-missing-placeholder} to disable it.
+
+This diagnostic warns for paths through the code in which a function in
+the @code{mktemp} family (@code{mkstemp}, @code{mkostemp},
+@code{mkstemps}, @code{mkostemps}, @code{mkdtemp}, @code{mktemp}) is
+called with a template string that does not contain the placeholder
+@samp{XXXXXX} at the expected position.
+
+@opindex Wanalyzer-mktemp-of-string-literal
+@opindex Wno-analyzer-mktemp-of-string-literal
+@item -Wno-analyzer-mktemp-of-string-literal
+This warning requires @option{-fanalyzer}, which enables it; use
+@option{-Wno-analyzer-mktemp-of-string-literal} to disable it.
+
+This diagnostic warns for paths through the code in which a function in
+the @code{mktemp} family (@code{mkstemp}, @code{mkostemp},
+@code{mkstemps}, @code{mkostemps}, @code{mkdtemp}, @code{mktemp}) is
+called on a string literal. Since these functions modify their argument
+in place, passing a string literal leads to undefined behavior.
+
+See @uref{https://wiki.sei.cmu.edu/confluence/x/VtYxBQ, STR30-C. Do not
attempt to modify string literals}.
+
@opindex Wanalyzer-out-of-bounds
@opindex Wno-analyzer-out-of-bounds
@item -Wno-analyzer-out-of-bounds
diff --git a/gcc/testsuite/gcc.dg/analyzer/mkdtemp-1.c
b/gcc/testsuite/gcc.dg/analyzer/mkdtemp-1.c
new file mode 100644
index 00000000000..0614fe2390c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/mkdtemp-1.c
@@ -0,0 +1,87 @@
+/* { dg-additional-options "-Wno-analyzer-null-argument" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void populate (char *buf);
+
+void test_passthrough (char *s)
+{
+ mkdtemp (s);
+}
+
+void test_string_literal_correct_placeholder (void)
+{
+ mkdtemp ("/var/tmp/dirXXXXXX"); /* { dg-warning "'mkdtemp' on a
string literal \\\[STR30-C\\\]" } */
+ /* { dg-message "use a writable character array" "fix suggestion" {
target *-*-* } .-1 } */
+}
+
+void test_string_literal_missing_placeholder (void)
+{
+ mkdtemp ("/var/tmp/dir"); /* { dg-warning "'mkdtemp' on a string
literal \\\[STR30-C\\\]" } */
+}
+
+void test_string_literal_empty (void)
+{
+ mkdtemp (""); /* { dg-warning "'mkdtemp' on a string literal
\\\[STR30-C\\\]" } */
+}
+
+void test_correct (void)
+{
+ char tmpl[] = "/var/tmp/mydir.XXXXXX";
+ mkdtemp (tmpl);
+}
+
+void test_correct_minimal (void)
+{
+ char tmpl[] = "XXXXXX";
+ mkdtemp (tmpl);
+}
+
+void test_correct_offset_into_buffer (void)
+{
+ char buf[] = "prefixXXXXXX";
+ mkdtemp (buf + 6);
+}
+
+void test_missing_placeholder (void)
+{
+ char tmpl[] = "/var/tmp/dir";
+ mkdtemp (tmpl); /* { dg-warning "'mkdtemp' template string does not
end with 'XXXXXX'" } */
+}
+
+void test_too_short (void)
+{
+ char tmpl[] = "XX";
+ mkdtemp (tmpl); /* { dg-warning "'mkdtemp' template string does not
end with 'XXXXXX'" } */
+}
+
+void test_empty_buffer (void)
+{
+ char tmpl[] = "";
+ mkdtemp (tmpl); /* { dg-warning "'mkdtemp' template string does not
end with 'XXXXXX'" } */
+}
+
+void test_partial_placeholder (void)
+{
+ char tmpl[] = "/var/tmp/dirXXXXX.";
+ mkdtemp (tmpl); /* { dg-warning "'mkdtemp' template string does not
end with 'XXXXXX'" } */
+}
+
+void test_four_xs (void)
+{
+ char tmpl[] = "/var/tmp/dirXXXX";
+ mkdtemp (tmpl); /* { dg-warning "'mkdtemp' template string does not
end with 'XXXXXX'" } */
+}
+
+void test_populated_buf (void)
+{
+ char tmpl[32];
+ populate (tmpl);
+ mkdtemp (tmpl);
+}
+
+void test_NULL (void)
+{
+ mkdtemp (NULL); /* possibly -Wanalyzer-null-argument */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/mkostemp-1.c
b/gcc/testsuite/gcc.dg/analyzer/mkostemp-1.c
new file mode 100644
index 00000000000..fcaecd2bd5a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/mkostemp-1.c
@@ -0,0 +1,125 @@
+/* { dg-additional-options "-Wno-analyzer-null-argument" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+extern int mkostemp (char *, int);
+extern void populate (char *buf);
+
+void test_passthrough (char *s)
+{
+ mkostemp (s, O_CLOEXEC);
+}
+
+void test_string_literal_correct_placeholder (void)
+{
+ mkostemp ("/tmp/testXXXXXX", O_CLOEXEC); /* { dg-warning "'mkostemp'
on a string literal \\\[STR30-C\\\]" } */
+ /* { dg-message "use a writable character array" "fix suggestion" {
target *-*-* } .-1 } */
+}
+
+void test_string_literal_missing_placeholder (void)
+{
+ mkostemp ("/tmp/test", O_CLOEXEC); /* { dg-warning "'mkostemp' on a
string literal \\\[STR30-C\\\]" } */
+}
+
+void test_string_literal_empty (void)
+{
+ mkostemp ("", 0); /* { dg-warning "'mkostemp' on a string literal
\\\[STR30-C\\\]" } */
+}
+
+void test_correct (void)
+{
+ char tmpl[] = "/tmp/test.XXXXXX";
+ mkostemp (tmpl, O_CLOEXEC);
+}
+
+void test_correct_minimal (void)
+{
+ char tmpl[] = "XXXXXX";
+ mkostemp (tmpl, 0);
+}
+
+void test_correct_offset_into_buffer (void)
+{
+ char buf[] = "////XXXXXX";
+ mkostemp (buf + 4, O_CLOEXEC);
+}
+
+void test_missing_placeholder (void)
+{
+ char tmpl[] = "/tmp/test";
+ mkostemp (tmpl, O_CLOEXEC); /* { dg-warning "'mkostemp' template
string does not end with 'XXXXXX'" } */
+}
+
+void test_too_short (void)
+{
+ char tmpl[] = "XXXX";
+ mkostemp (tmpl, 0); /* { dg-warning "'mkostemp' template string does
not end with 'XXXXXX'" } */
+}
+
+void test_empty_buffer (void)
+{
+ char tmpl[] = "";
+ mkostemp (tmpl, O_CLOEXEC); /* { dg-warning "'mkostemp' template
string does not end with 'XXXXXX'" } */
+}
+
+void test_trailing_nul_after_placeholder (void)
+{
+ char tmpl[] = "/tmp/testXXXXXXz";
+ mkostemp (tmpl, 0); /* { dg-warning "'mkostemp' template string does
not end with 'XXXXXX'" } */
+}
+
+void test_five_xs (void)
+{
+ char tmpl[] = "/tmp/testXXXXX";
+ mkostemp (tmpl, O_CLOEXEC); /* { dg-warning "'mkostemp' template
string does not end with 'XXXXXX'" } */
+}
+
+void test_populated_buf (void)
+{
+ char tmpl[24];
+ populate (tmpl);
+ mkostemp (tmpl, O_CLOEXEC);
+}
+
+void test_NULL (void)
+{
+ mkostemp (NULL, 0); /* possibly -Wanalyzer-null-argument */
+}
+
+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" } */
+}
+
+void test_redundant_o_creat (void)
+{
+ char tmpl[] = "/tmp/testXXXXXX";
+ mkostemp (tmpl, O_CREAT); /* { dg-warning "'mkostemp' flags argument
should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these are already
implied" } */
+}
+
+void test_redundant_o_excl (void)
+{
+ char tmpl[] = "/tmp/testXXXXXX";
+ mkostemp (tmpl, O_EXCL); /* { dg-warning "'mkostemp' flags argument
should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these are already
implied" } */
+}
+
+void test_redundant_combined (void)
+{
+ char tmpl[] = "/tmp/testXXXXXX";
+ mkostemp (tmpl, O_RDWR | O_CREAT); /* { dg-warning "'mkostemp' flags
argument should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these
are already implied" } */
+}
+
+void test_valid_flags (void)
+{
+ char tmpl[] = "/tmp/testXXXXXX";
+ mkostemp (tmpl, O_CLOEXEC);
+}
+
+void test_zero_flags (void)
+{
+ char tmpl[] = "/tmp/testXXXXXX";
+ mkostemp (tmpl, 0);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/mkostemps-1.c
b/gcc/testsuite/gcc.dg/analyzer/mkostemps-1.c
new file mode 100644
index 00000000000..4e259c67a8f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/mkostemps-1.c
@@ -0,0 +1,126 @@
+/* { dg-additional-options "-Wno-analyzer-null-argument" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+extern int mkostemps (char *, int, int);
+extern void populate (char *buf);
+
+void test_passthrough (char *s)
+{
+ mkostemps (s, 3, O_CLOEXEC);
+}
+
+void test_string_literal_correct_placeholder (void)
+{
+ mkostemps ("/tmp/dataXXXXXX.db", 3, O_CLOEXEC); /* { dg-warning
"'mkostemps' on a string literal \\\[STR30-C\\\]" } */
+ /* { dg-message "use a writable character array" "fix suggestion" {
target *-*-* } .-1 } */
+}
+
+void test_string_literal_missing_placeholder (void)
+{
+ mkostemps ("/tmp/data.db", 3, O_CLOEXEC); /* { dg-warning
"'mkostemps' on a string literal \\\[STR30-C\\\]" } */
+}
+
+void test_string_literal_empty (void)
+{
+ mkostemps ("", 0, 0); /* { dg-warning "'mkostemps' on a string
literal \\\[STR30-C\\\]" } */
+}
+
+void test_correct_with_suffix (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, O_CLOEXEC);
+}
+
+void test_correct_zero_suffix (void)
+{
+ char tmpl[] = "/tmp/logXXXXXX";
+ mkostemps (tmpl, 0, O_CLOEXEC);
+}
+
+void test_correct_long_suffix (void)
+{
+ char tmpl[] = "XXXXXX.json";
+ mkostemps (tmpl, 5, 0);
+}
+
+void test_missing_placeholder_with_suffix (void)
+{
+ char tmpl[] = "/tmp/data.json";
+ mkostemps (tmpl, 5, O_CLOEXEC); /* { dg-warning "'mkostemps' template
string does not contain 'XXXXXX' before a 5-character suffix" } */
+}
+
+void test_placeholder_at_wrong_position (void)
+{
+ /* "XXXXXX" is at the end, but suffixlen says 2 chars should follow
it. */
+ char tmpl[] = "/tmp/dataXXXXXX";
+ mkostemps (tmpl, 2, 0); /* { dg-warning "'mkostemps' template string
does not contain 'XXXXXX' before a 2-character suffix" } */
+}
+
+void test_too_short_for_suffix (void)
+{
+ char tmpl[] = "XY";
+ mkostemps (tmpl, 1, O_CLOEXEC); /* { dg-warning "'mkostemps' template
string does not contain 'XXXXXX' before a 1-character suffix" } */
+}
+
+void test_empty_buffer_with_suffix (void)
+{
+ char tmpl[] = "";
+ mkostemps (tmpl, 2, 0); /* { dg-warning "'mkostemps' template string
does not contain 'XXXXXX' before a 2-character suffix" } */
+}
+
+void test_suffix_consumes_placeholder (void)
+{
+ char tmpl[] = "XXXXXX.c";
+ mkostemps (tmpl, 6, O_CLOEXEC); /* { dg-warning "'mkostemps' template
string does not contain 'XXXXXX' before a 6-character suffix" } */
+}
+
+void test_populated_buf (void)
+{
+ char tmpl[28];
+ populate (tmpl);
+ mkostemps (tmpl, 3, O_CLOEXEC);
+}
+
+void test_NULL (void)
+{
+ mkostemps (NULL, 0, 0); /* possibly -Wanalyzer-null-argument */
+}
+
+void test_redundant_o_rdwr (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, O_RDWR); /* { dg-warning "'mkostemps' flags
argument should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these
are already implied" } */
+}
+
+void test_redundant_o_creat (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, O_CREAT); /* { dg-warning "'mkostemps' flags
argument should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these
are already implied" } */
+}
+
+void test_redundant_o_excl (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, O_EXCL); /* { dg-warning "'mkostemps' flags
argument should not include 'O_RDWR', 'O_CREAT', or 'O_EXCL' as these
are already implied" } */
+}
+
+void test_redundant_all_three (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, O_RDWR | O_CREAT | O_EXCL); /* { dg-warning
"'mkostemps' flags argument should not include 'O_RDWR', 'O_CREAT', or
'O_EXCL' as these are already implied" } */
+}
+
+void test_valid_flags (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, O_CLOEXEC);
+}
+
+void test_zero_flags (void)
+{
+ char tmpl[] = "/tmp/dataXXXXXX.db";
+ mkostemps (tmpl, 3, 0);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/mkstemp-1.c
b/gcc/testsuite/gcc.dg/analyzer/mkstemp-1.c
index 2eda175f29f..dc8ce9e2b92 100644
--- a/gcc/testsuite/gcc.dg/analyzer/mkstemp-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/mkstemp-1.c
@@ -10,13 +10,13 @@ void test_passthrough (char *s)
mkstemp (s);
}
-void test_string_literal_correct_suffix (void)
+void test_string_literal_correct_placeholder (void)
{
mkstemp ("/tmp/fooXXXXXX"); /* { dg-warning "'mkstemp' on a string
literal \\\[STR30-C\\\]" } */
/* { dg-message "use a writable character array" "fix suggestion" {
target *-*-* } .-1 } */
}
-void test_string_literal_missing_suffix (void)
+void test_string_literal_missing_placeholder (void)
{
mkstemp ("/tmp/foo"); /* { dg-warning "'mkstemp' on a string literal
\\\[STR30-C\\\]" } */
}
@@ -41,18 +41,18 @@ void test_correct_minimal (void)
void test_correct_offset_into_buffer (void)
{
char buf[] = "/tmp/XXXXXX";
- /* Suffix is still correct from the pointer's perspective. */
+ /* Placeholder is still correct from the pointer's perspective. */
mkstemp (buf + 5);
}
-void test_missing_suffix_offset_into_buffer (void)
+void test_missing_placeholder_offset_into_buffer (void)
{
char buf[] = "/tmp/XXXXXX";
- /* Suffix is incorrect from the pointer's perspective. */
+ /* Placeholder is incorrect from the pointer's perspective. */
mkstemp (buf + 6); /* { dg-warning "'mkstemp' template string does
not end with 'XXXXXX'" } */
}
-void test_missing_suffix (void)
+void test_missing_placeholder (void)
{
char tmpl[] = "/tmp/foo";
mkstemp (tmpl); /* { dg-warning "'mkstemp' template string does not
end with 'XXXXXX'" } */
@@ -70,7 +70,7 @@ void test_empty_buffer (void)
mkstemp (tmpl); /* { dg-warning "'mkstemp' template string does not
end with 'XXXXXX'" } */
}
-void test_partial_suffix (void)
+void test_partial_placeholder (void)
{
char tmpl[] = "/tmp/fooXXXXX_";
mkstemp (tmpl); /* { dg-warning "'mkstemp' template string does not
end with 'XXXXXX'" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/mkstemps-1.c
b/gcc/testsuite/gcc.dg/analyzer/mkstemps-1.c
new file mode 100644
index 00000000000..c9172122c30
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/mkstemps-1.c
@@ -0,0 +1,88 @@
+/* { dg-additional-options "-Wno-analyzer-null-argument" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void populate (char *buf);
+
+void test_passthrough (char *s)
+{
+ mkstemps (s, 4);
+}
+
+void test_string_literal_correct_placeholder (void)
+{
+ mkstemps ("/tmp/fooXXXXXX.txt", 4); /* { dg-warning "'mkstemps' on a
string literal \\\[STR30-C\\\]" } */
+ /* { dg-message "use a writable character array" "fix suggestion" {
target *-*-* } .-1 } */
+}
+
+void test_string_literal_missing_placeholder (void)
+{
+ mkstemps ("/tmp/foo.txt", 4); /* { dg-warning "'mkstemps' on a string
literal \\\[STR30-C\\\]" } */
+}
+
+void test_string_literal_empty (void)
+{
+ mkstemps ("", 0); /* { dg-warning "'mkstemps' on a string literal
\\\[STR30-C\\\]" } */
+}
+
+void test_correct_with_suffix (void)
+{
+ char tmpl[] = "/tmp/fooXXXXXX.txt";
+ mkstemps (tmpl, 4);
+}
+
+void test_correct_zero_suffix (void)
+{
+ char tmpl[] = "/tmp/barXXXXXX";
+ mkstemps (tmpl, 0);
+}
+
+void test_correct_single_char_suffix (void)
+{
+ char tmpl[] = "XXXXXXZ";
+ mkstemps (tmpl, 1);
+}
+
+void test_missing_placeholder_with_suffix (void)
+{
+ char tmpl[] = "/tmp/foo.conf";
+ mkstemps (tmpl, 5); /* { dg-warning "'mkstemps' template string does
not contain 'XXXXXX' before a 5-character suffix" } */
+}
+
+void test_placeholder_at_wrong_position (void)
+{
+ /* "XXXXXX" is at the end, but suffixlen says 3 chars should follow
it. */
+ char tmpl[] = "/tmp/fooXXXXXX";
+ mkstemps (tmpl, 3); /* { dg-warning "'mkstemps' template string does
not contain 'XXXXXX' before a 3-character suffix" } */
+}
+
+void test_too_short_for_suffix (void)
+{
+ char tmpl[] = "abc";
+ mkstemps (tmpl, 2); /* { dg-warning "'mkstemps' template string does
not contain 'XXXXXX' before a 2-character suffix" } */
+}
+
+void test_empty_buffer_with_suffix (void)
+{
+ char tmpl[] = "";
+ mkstemps (tmpl, 3); /* { dg-warning "'mkstemps' template string does
not contain 'XXXXXX' before a 3-character suffix" } */
+}
+
+void test_suffix_too_large (void)
+{
+ char tmpl[] = "XXXXXXAB";
+ mkstemps (tmpl, 4); /* { dg-warning "'mkstemps' template string does
not contain 'XXXXXX' before a 4-character suffix" } */
+}
+
+void test_populated_buf (void)
+{
+ char tmpl[30];
+ populate (tmpl);
+ mkstemps (tmpl, 4);
+}
+
+void test_NULL (void)
+{
+ mkstemps (NULL, 0); /* possibly -Wanalyzer-null-argument */
+}
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. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void populate (char *buf);
+
+void test_passthrough (char *s)
+{
+ mktemp (s);
+}
+
+void test_string_literal_correct_placeholder (void)
+{
+ mktemp ("/home/user/sessXXXXXX"); /* { dg-warning "'mktemp' on a
string literal \\\[STR30-C\\\]" } */
+ /* { dg-message "use a writable character array" "fix suggestion" {
target *-*-* } .-1 } */
+}
+
+void test_string_literal_missing_placeholder (void)
+{
+ mktemp ("/home/user/sess"); /* { dg-warning "'mktemp' on a string
literal \\\[STR30-C\\\]" } */
+}
+
+void test_string_literal_empty (void)
+{
+ mktemp (""); /* { dg-warning "'mktemp' on a string literal
\\\[STR30-C\\\]" } */
+}
+
+void test_correct (void)
+{
+ char tmpl[] = "/var/run/sock.XXXXXX";
+ mktemp (tmpl);
+}
+
+void test_correct_minimal (void)
+{
+ char tmpl[] = "XXXXXX";
+ mktemp (tmpl);
+}
+
+void test_correct_offset_into_buffer (void)
+{
+ char buf[] = "////XXXXXX";
+ mktemp (buf + 4);
+}
+
+void test_missing_placeholder_offset_into_buffer (void)
+{
+ char buf[] = "////XXXXXX";
+ /* Placeholder is incorrect from the pointer's perspective. */
+ mktemp (buf + 5); /* { dg-warning "'mktemp' template string does not
end with 'XXXXXX'" } */
+}
+
+void test_missing_placeholder (void)
+{
+ char tmpl[] = "/var/run/sock";
+ mktemp (tmpl); /* { dg-warning "'mktemp' template string does not end
with 'XXXXXX'" } */
+}
+
+void test_too_short (void)
+{
+ char tmpl[] = "XY";
+ mktemp (tmpl); /* { dg-warning "'mktemp' template string does not end
with 'XXXXXX'" } */
+}
+
+void test_empty_buffer (void)
+{
+ char tmpl[] = "";
+ mktemp (tmpl); /* { dg-warning "'mktemp' template string does not end
with 'XXXXXX'" } */
+}
+
+void test_partial_placeholder (void)
+{
+ char tmpl[] = "/var/run/sockXXXXX-";
+ mktemp (tmpl); /* { dg-warning "'mktemp' template string does not end
with 'XXXXXX'" } */
+}
+
+void test_four_xs (void)
+{
+ char tmpl[] = "/var/run/sockXXXX";
+ mktemp (tmpl); /* { dg-warning "'mktemp' template string does not end
with 'XXXXXX'" } */
+}
+
+void test_populated_buf (void)
+{
+ char tmpl[24];
+ populate (tmpl);
+ mktemp (tmpl);
+}
+
+void test_NULL (void)
+{
+ mktemp (NULL); /* possibly -Wanalyzer-null-argument */
+}
--
2.52.0