On Mon, 2023-08-28 at 17:13 +0200, Benjamin Priour wrote: > 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.
Aha - good debugging! Your proposed fix sounds worth investigating. Let me know if you'd like help with it. Thanks Dave