Hi,

Test gcc.dg/analyzer/pr94362-1.c actually has an additional null_deref
> warning in C++, which is not affected by exceptions
> or optimizations. I will keep updated on that one. Note that no warnings
> are emitted for this test in C.
>
> analyzer/pr94362-1.c: In function ‘const EVP_PKEY_ASN1_METHOD*
> EVP_PKEY_asn1_find_str(ENGINE**, const char*, int)’:
> analyzer/pr94362-1.c:55:16: warning: dereference of NULL ‘ameth’ [CWE-476]
> [-Wanalyzer-null-dereference]
> analyzer/pr94362-1.c:39:29: note: (1) entry to ‘EVP_PKEY_asn1_find_str’
> analyzer/pr94362-1.c:42:31: note: (2) ‘ameth’ is NULL
> analyzer/pr94362-1.c:53:43: note: (3) following ‘true’ branch...
> analyzer/pr94362-1.c:54:31: note: (4) ...to here
> analyzer/pr94362-1.c:54:31: note: (5) calling ‘EVP_PKEY_asn1_get0’ from
> ‘EVP_PKEY_asn1_find_str’
> analyzer/pr94362-1.c:29:29: note: (6) entry to ‘EVP_PKEY_asn1_get0’
> analyzer/pr94362-1.c:32:53: note: (7) ‘0’ is NULL
> analyzer/pr94362-1.c:54:31: note: (8) returning to
> ‘EVP_PKEY_asn1_find_str’ from ‘EVP_PKEY_asn1_get0’
> analyzer/pr94362-1.c:54:31: note: (9) ‘ameth’ is NULL
> analyzer/pr94362-1.c:55:16: note: (10) dereference of NULL ‘ameth’
>
>
> Cheers,
> Benjamin.
>
>
As promised a quick update on that front. I've managed to pinpoint the
difference between the C and C++.
Compiling with -fno-exceptions -O0 the two IPA's seen by the analyzer are
nearly identical, if not for one difference.

const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_find_str(ENGINE **pe, const char
*str,
                                                   int len)
{
  int i;
  const EVP_PKEY_ASN1_METHOD *ameth = (const EVP_PKEY_ASN1_METHOD *) ((void
*)0);
  ...
  for (i = EVP_PKEY_asn1_get_count(); i-- > 0;) {
    ameth = EVP_PKEY_asn1_get0(i); /* At this point, i >= 0 */
    if (ameth->pkey_flags & 0x1)
      continue;
    return ameth;
  }
  ...
}

IPA when compiled by as C:

 <bb 6> :
  i_23 = EVP_PKEY_asn1_get_count ();
  goto <bb 10>; [INV]

  <bb 7> :
  ameth_27 = EVP_PKEY_asn1_get0 (i_24);
  _2 = ameth_27->pkey_flags;
  _3 = _2 & 1;
  if (_3 != 0)
    goto <bb 8>; [INV]
  else
    goto <bb 9>; [INV]

  <bb 8> :
  // predicted unlikely by continue predictor.
  goto <bb 10>; [INV]

  <bb 9> :
  _28 = ameth_27;
  goto <bb 12>; [INV]

  <bb 10> :
  # i_5 = PHI <i_23(6), i_24(8)>
  i.4_4 = i_5;
  i_24 = i.4_4 + -1;
  if (i.4_4 > 0)
    goto <bb 7>; [INV]
  else
    goto <bb 11>; [INV]

... and as C++

<bb 6> :
  i_23 = EVP_PKEY_asn1_get_count ();
  goto <bb 10>; [INV]

  <bb 7> :
  ameth_28 = EVP_PKEY_asn1_get0 (i_24);
  _2 = ameth_28->pkey_flags;
  _3 = _2 & 1;
  if (_3 != 0)
    goto <bb 8>; [INV]
  else
    goto <bb 9>; [INV]

  <bb 8> :
  // predicted unlikely by continue predictor.
  goto <bb 10>; [INV]

  <bb 9> :
  _29 = ameth_28;
  goto <bb 12>; [INV]

  <bb 10> :
  # i_5 = PHI <i_23(6), i_24(8)>
  i.5_4 = i_5;
  i_24 = i.5_4 + -1;
  retval.4_25 = i.5_4 > 0;       /* Difference here. C++ stores the
comparison's result before using it */
  if (retval.4_25 != 0)
    goto <bb 7>; [INV]
  else
    goto <bb 11>; [INV]

This slight difference causes i_24 to not be part of an equivalence class
in C++, although it is part of one in C.
Therefore when calling ameth_28 = EVP_PKEY_asn1_get0 (i_24);, i_24 won't
appear as constrained in C++,
although we know it to be positive. Info is lost.
Further down the line, because of this missing ec
constraint_manager::eval_condition will evaluate to UNKNOWN,
and the pending diagnostic path won't be refused but accepted as feasible
in C++.

constraints and equivalence classes in C:

ec2: {(CONJURED(_8 = sk_EVP_PKEY_ASN1_METHOD_num (app_methos.1_2);,
_8)+(int)1)}
ec3: {(int)0 == [m_constant]'0'} /* int since constraining 'int i_24' */
ec4: {CONJURED(_8 = sk_EVP_PKEY_ASN1_METHOD_num (app_methos.1_2);, _8)} /*
This is the ec of i_24 */
ec5: {(int)-1 == [m_constant]'-1'}
constraints:
1: ec5 < ec4
2: ec3 < ec2

... vs C++'s:

ec2: {((CONJURED(_8 = sk_EVP_PKEY_ASN1_METHOD_num (app_methos.1_2);,
_8)+(int)1)>(int)0)}
ec3: {(bool)0 == [m_constant]'0'} /* bool instead of int cuz constraining
'bool retval.4_25' */
constraints:
1: ec2 != ec3

As you can see, i_24 equivalence class does not appear in C++.
I'm considering if in a condition lhs or rhs is a logical binop svalue, we
should unwrap one level, so that the condition's constraint is actually
applied on
the inner svalue.

Cheers,
Benjamin.

Reply via email to