On Fri, Mar 17, 2023 at 01:55:51PM +0000, Richard Biener wrote:
> > Anyway, I think it is fine to implement __builtin_expect this way
> > for now, ERF_RETURNS_ARG will be more important for pointers, especially if
> > we propagate something more than just maybe be/can't be/must be null.
> > Don't you need to handle BUILT_IN_EXPECT_WITH_PROBABILITY the same though?
>
> Yes, BUILT_IN_ASSUME_ALIGNED would be another candidate.
BUILT_IN_ASSUME_ALIGNED doesn't do that intentionally.
E.g. tree-object-size.cc (pass_through_call) comments on this:
unsigned rf = gimple_call_return_flags (call);
if (rf & ERF_RETURNS_ARG)
{
unsigned argnum = rf & ERF_RETURN_ARG_MASK;
if (argnum < gimple_call_num_args (call))
return gimple_call_arg (call, argnum);
}
/* __builtin_assume_aligned is intentionally not marked RET1. */
if (gimple_call_builtin_p (call, BUILT_IN_ASSUME_ALIGNED))
return gimple_call_arg (call, 0);
The reason is that we don't want passes to propagate the argument to the
return value but use a different SSA_NAME there, so that we can have
there different alignment info.
And as you show on the testcases, it probably isn't a good idea for
BUILT_IN_EXPECT* either.
So, perhaps use op_cfn_pass_through_arg1 for the ERF_RETURNS_ARG functions
and BUILT_IN_EXPECT* ?
> >From feb846cbff9774125d8401dfeacd8a4b9c2dccfa Mon Sep 17 00:00:00 2001
> From: Richard Biener <[email protected]>
> Date: Fri, 17 Mar 2023 13:14:49 +0100
> Subject: [PATCH] tree-optimization/109170 - bogus use-after-free with
> __builtin_expect
> To: [email protected]
>
> The following adds a missing range-op for __builtin_expect which
> helps -Wuse-after-free to detect the case a realloc original
> pointer is used when the result was NULL. The implementation
> should handle all argument one pass-through builtins we handle
> in the fnspec machinery.
>
> tree-optimization/109170
> * gimple-range-op.cc (cfn_pass_through_arg1): New.
> (gimple_range_op_handler::maybe_builtin_call): Handle
> __builtin_expect and similar via cfn_pass_through_arg1
> and inspecting the calls fnspec.
> * builtins.cc (builtin_fnspec): Handle BUILT_IN_EXPECT
> and BUILT_IN_EXPECT_WITH_PROBABILITY.
>
> * gcc.dg/Wuse-after-free-pr109170.c: New testcase.
> ---
> gcc/builtins.cc | 2 ++
> gcc/gimple-range-op.cc | 32 ++++++++++++++++++-
> .../gcc.dg/Wuse-after-free-pr109170.c | 15 +++++++++
> 3 files changed, 48 insertions(+), 1 deletion(-)
> create mode 100644 gcc/testsuite/gcc.dg/Wuse-after-free-pr109170.c
>
> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
> index 90246e214d6..56545027297 100644
> --- a/gcc/builtins.cc
> +++ b/gcc/builtins.cc
> @@ -11715,6 +11715,8 @@ builtin_fnspec (tree callee)
> case BUILT_IN_RETURN_ADDRESS:
> return ".c";
> case BUILT_IN_ASSUME_ALIGNED:
> + case BUILT_IN_EXPECT:
> + case BUILT_IN_EXPECT_WITH_PROBABILITY:
> return "1cX ";
> /* But posix_memalign stores a pointer into the memory pointed to
> by its first argument. */
> diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc
> index a5d625387e7..1a00f1690e5 100644
> --- a/gcc/gimple-range-op.cc
> +++ b/gcc/gimple-range-op.cc
> @@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
> #include "range.h"
> #include "value-query.h"
> #include "gimple-range.h"
> +#include "attr-fnspec.h"
>
> // Given stmt S, fill VEC, up to VEC_SIZE elements, with relevant ssa-names
> // on the statement. For efficiency, it is an error to not pass in enough
> @@ -309,6 +310,26 @@ public:
> }
> } op_cfn_constant_p;
>
> +// Implement range operator for integral/pointer functions returning
> +// the first argument.
> +class cfn_pass_through_arg1 : public range_operator
> +{
> +public:
> + using range_operator::fold_range;
> + virtual bool fold_range (irange &r, tree, const irange &lh,
> + const irange &, relation_trio) const
> + {
> + r = lh;
> + return true;
> + }
> + virtual bool op1_range (irange &r, tree, const irange &lhs,
> + const irange &, relation_trio) const
> + {
> + r = lhs;
> + return true;
> + }
> +} op_cfn_pass_through_arg1;
> +
> // Implement range operator for CFN_BUILT_IN_SIGNBIT.
> class cfn_signbit : public range_operator_float
> {
> @@ -967,6 +988,15 @@ gimple_range_op_handler::maybe_builtin_call ()
> break;
>
> default:
> - break;
> + {
> + unsigned arg;
> + if (gimple_call_fnspec (call).returns_arg (&arg) && arg == 0)
> + {
> + m_valid = true;
> + m_op1 = gimple_call_arg (call, 0);
> + m_int = &op_cfn_pass_through_arg1;
> + }
> + break;
> + }
> }
> }
> diff --git a/gcc/testsuite/gcc.dg/Wuse-after-free-pr109170.c
> b/gcc/testsuite/gcc.dg/Wuse-after-free-pr109170.c
> new file mode 100644
> index 00000000000..fa7dc66d66c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/Wuse-after-free-pr109170.c
> @@ -0,0 +1,15 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -Wuse-after-free" } */
> +
> +unsigned long bufmax = 0;
> +unsigned long __open_catalog_bufmax;
> +void *realloc(void *, __SIZE_TYPE__);
> +void free(void *);
> +
> +void __open_catalog(char *buf)
> +{
> + char *old_buf = buf;
> + buf = realloc (buf, bufmax);
> + if (__builtin_expect ((buf == ((void *)0)), 0))
> + free (old_buf); /* { dg-bogus "used after" } */
> +}
> --
> 2.35.3
Jakub