Thread: jsonapi: scary new warnings with LTO enabled
I noticed some new warnings from buildfarm member chafer, which I'm able to reproduce locally on a Fedora 41 box by building with "meson setup build -Db_lto=true": ninja: Entering directory `build' [1515/2472] Linking target src/interfaces/libpq/libpq.so.5.18 In function 'freeJsonLexContext', inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:688:1, inlined from 'handle_oauth_sasl_error' at ../src/interfaces/libpq/fe-auth-oauth.c:547:2: ../src/common/jsonapi.c:723:17: warning: 'free' called on unallocated object 'lex' [-Wfree-nonheap-object] 723 | FREE(lex); | ^ ../src/interfaces/libpq/fe-auth-oauth.c: In function 'handle_oauth_sasl_error': ../src/interfaces/libpq/fe-auth-oauth.c:479:24: note: declared here 479 | JsonLexContext lex = {0}; | ^ [2407/2472] Linking target src/test/modules/test_json_parser/test_json_parser_incremental_shlib In function 'freeJsonLexContext', inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:688:1, inlined from 'main' at ../src/test/modules/test_json_parser/test_json_parser_incremental.c:198:2: ../src/common/jsonapi.c:723:17: warning: 'free' called on unallocated object 'lex' [-Wfree-nonheap-object] 723 | FREE(lex); | ^ ../src/test/modules/test_json_parser/test_json_parser_incremental.c: In function 'main': ../src/test/modules/test_json_parser/test_json_parser_incremental.c:87:24: note: declared here 87 | JsonLexContext lex; | ^ [2426/2472] Linking target src/test/modules/test_json_parser/test_json_parser_incremental In function 'pg_free', inlined from 'pfree' at ../src/common/fe_memutils.c:135:2, inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:723:3, inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:688:1, inlined from 'main' at ../src/test/modules/test_json_parser/test_json_parser_incremental.c:198:2: ../src/common/fe_memutils.c:107:9: warning: 'free' called on unallocated object 'lex' [-Wfree-nonheap-object] 107 | free(ptr); | ^ ../src/test/modules/test_json_parser/test_json_parser_incremental.c: In function 'main': ../src/test/modules/test_json_parser/test_json_parser_incremental.c:87:24: note: declared here 87 | JsonLexContext lex; | ^ AFAICT there is no actual bug here: the FREE() call is reached only if the JSONLEX_FREE_STRUCT flag is set, which it should not be for these call sites. But evidently the LTO optimizer is not quite smart enough to realize that. It seems fairly dangerous to ignore -Wfree-nonheap-object warnings. I feel like we ought to move to prevent these somehow. I'm not sure how other than giving up on stack allocation of JsonLexContexts, though, especially if we consider the jsonapi API frozen. But seeing that there are only three such call sites and none of them seem in the least performance-critical, maybe we should just do that? regards, tom lane
> On 16 Apr 2025, at 23:42, Tom Lane <tgl@sss.pgh.pa.us> wrote: > It seems fairly dangerous to ignore -Wfree-nonheap-object warnings. > I feel like we ought to move to prevent these somehow. Absolutely agree. > I'm not sure > how other than giving up on stack allocation of JsonLexContexts, > though, especially if we consider the jsonapi API frozen. But seeing > that there are only three such call sites and none of them seem in the > least performance-critical, maybe we should just do that? I can't see any other option really, and there is no performance angle really so that should be safe. Since I committed at least one of these, let me know if you want me to tackle it. -- Daniel Gustafsson
Daniel Gustafsson <daniel@yesql.se> writes: >> On 16 Apr 2025, at 23:42, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> I'm not sure >> how other than giving up on stack allocation of JsonLexContexts, >> though, especially if we consider the jsonapi API frozen. But seeing >> that there are only three such call sites and none of them seem in the >> least performance-critical, maybe we should just do that? > I can't see any other option really, and there is no performance angle really > so that should be safe. Since I committed at least one of these, let me know > if you want me to tackle it. The only alternative I can see that might stop the warning is if we can find a way to make it clearer to the optimizer that the FREE() isn't reached. But I'm not sure about a trustworthy way to make that happen. Maybe it'd work to change the signature of freeJsonLexContext (or perhaps better, add a separate entry point) so that the caller is passing a bool constant that controls whether to free the struct. We could have an Assert that compares that to the state of the JSONLEX_FREE_STRUCT flag to catch mistakes. This seems kind of messy though. regards, tom lane
Em qua., 16 de abr. de 2025 às 18:42, Tom Lane <tgl@sss.pgh.pa.us> escreveu:
I noticed some new warnings from buildfarm member chafer,
which I'm able to reproduce locally on a Fedora 41 box
by building with "meson setup build -Db_lto=true":
ninja: Entering directory `build'
[1515/2472] Linking target src/interfaces/libpq/libpq.so.5.18
In function 'freeJsonLexContext',
inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:688:1,
inlined from 'handle_oauth_sasl_error' at ../src/interfaces/libpq/fe-auth-oauth.c:547:2:
../src/common/jsonapi.c:723:17: warning: 'free' called on unallocated object 'lex' [-Wfree-nonheap-object]
723 | FREE(lex);
| ^
../src/interfaces/libpq/fe-auth-oauth.c: In function 'handle_oauth_sasl_error':
../src/interfaces/libpq/fe-auth-oauth.c:479:24: note: declared here
479 | JsonLexContext lex = {0};
| ^
[2407/2472] Linking target src/test/modules/test_json_parser/test_json_parser_incremental_shlib
In function 'freeJsonLexContext',
inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:688:1,
inlined from 'main' at ../src/test/modules/test_json_parser/test_json_parser_incremental.c:198:2:
../src/common/jsonapi.c:723:17: warning: 'free' called on unallocated object 'lex' [-Wfree-nonheap-object]
723 | FREE(lex);
| ^
../src/test/modules/test_json_parser/test_json_parser_incremental.c: In function 'main':
../src/test/modules/test_json_parser/test_json_parser_incremental.c:87:24: note: declared here
87 | JsonLexContext lex;
| ^
[2426/2472] Linking target src/test/modules/test_json_parser/test_json_parser_incremental
In function 'pg_free',
inlined from 'pfree' at ../src/common/fe_memutils.c:135:2,
inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:723:3,
inlined from 'freeJsonLexContext' at ../src/common/jsonapi.c:688:1,
inlined from 'main' at ../src/test/modules/test_json_parser/test_json_parser_incremental.c:198:2:
../src/common/fe_memutils.c:107:9: warning: 'free' called on unallocated object 'lex' [-Wfree-nonheap-object]
107 | free(ptr);
| ^
../src/test/modules/test_json_parser/test_json_parser_incremental.c: In function 'main':
../src/test/modules/test_json_parser/test_json_parser_incremental.c:87:24: note: declared here
87 | JsonLexContext lex;
| ^
AFAICT there is no actual bug here: the FREE() call is reached only if
the JSONLEX_FREE_STRUCT flag is set, which it should not be for these
call sites.
See the function *makeJsonLexContextCstringLen* (line 400)
The JSONLEX_FREE_STRUCT is enabled, no?
fe-auth-oauth.c (line 507)
makeJsonLexContextCstringLen(&lex, msg, msglen, PG_UTF8, true);
Worst, on a second call, with lex not NULL, the flags is reseted
and the struct will no longer be released?
best regards,
Ranier Vilela
> On 17 Apr 2025, at 00:12, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Daniel Gustafsson <daniel@yesql.se> writes: >>> On 16 Apr 2025, at 23:42, Tom Lane <tgl@sss.pgh.pa.us> wrote: >>> I'm not sure >>> how other than giving up on stack allocation of JsonLexContexts, >>> though, especially if we consider the jsonapi API frozen. But seeing >>> that there are only three such call sites and none of them seem in the >>> least performance-critical, maybe we should just do that? > >> I can't see any other option really, and there is no performance angle really >> so that should be safe. Since I committed at least one of these, let me know >> if you want me to tackle it. > > The only alternative I can see that might stop the warning is if we > can find a way to make it clearer to the optimizer that the FREE() > isn't reached. But I'm not sure about a trustworthy way to make that > happen. Maybe it'd work to change the signature of freeJsonLexContext > (or perhaps better, add a separate entry point) so that the caller is > passing a bool constant that controls whether to free the struct. > We could have an Assert that compares that to the state of the > JSONLEX_FREE_STRUCT flag to catch mistakes. This seems kind of messy > though. Yeah, that seems messy enough that someone down the line will go "why on earth" and we'll have to revisit this discussion. It can probably be made to work but I doubt it will be worth it compared to allocating on the heap. -- Daniel Gustafsson
Daniel Gustafsson <daniel@yesql.se> writes: > On 17 Apr 2025, at 00:12, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> The only alternative I can see that might stop the warning is if we >> can find a way to make it clearer to the optimizer that the FREE() >> isn't reached. But I'm not sure about a trustworthy way to make that >> happen. > Yeah, that seems messy enough that someone down the line will go "why on earth" > and we'll have to revisit this discussion. It can probably be made to work but > I doubt it will be worth it compared to allocating on the heap. Looking through all of the callers of freeJsonLexContext, quite a lot of them use local JsonLexContext structs, and probably some of them are more performance-critical than these. So that raises the question of why are we seeing warnings for only these call sites? Maybe there is a more elegant way to suppress them. Still, I think that observation refutes my initial thought that we should rip out support for local JsonLexContext structs altogether. I'm inclined now to just do the minimal thing of changing these callers to use an allocated struct, and call it a day. (BTW, there seem to be only 2 places to change not 3; two of the warnings are pointing at the same variable.) regards, tom lane
On Wed, Apr 16, 2025 at 4:04 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: > Looking through all of the callers of freeJsonLexContext, quite > a lot of them use local JsonLexContext structs, and probably some > of them are more performance-critical than these. So that raises > the question of why are we seeing warnings for only these call > sites? Yeah, I had the same question... > Maybe there is a more elegant way to suppress them. Can we brute-force ignore this particular warning site with a #pragma (suggested in [1])? --Jacob [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98753
Jacob Champion <jacob.champion@enterprisedb.com> writes: > On Wed, Apr 16, 2025 at 4:04 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> Looking through all of the callers of freeJsonLexContext, quite >> a lot of them use local JsonLexContext structs, and probably some >> of them are more performance-critical than these. So that raises >> the question of why are we seeing warnings for only these call >> sites? > Yeah, I had the same question... After making another pass through the callers of freeJsonLexContext, I observe that the warnings appear in callers that use a local variable *and* contain goto statements. So I'm betting that the presence of goto's causes the LTO optimizer to pull in its horns quite a bit and thereby fail to detect the flag correlation. >> Maybe there is a more elegant way to suppress them. > Can we brute-force ignore this particular warning site with a #pragma > (suggested in [1])? That's surely not elegant :-(. However, I don't especially want to rewrite away the goto's in these callers ... regards, tom lane
> On 17 Apr 2025, at 01:28, Tom Lane <tgl@sss.pgh.pa.us> wrote: > > Jacob Champion <jacob.champion@enterprisedb.com> writes: >> On Wed, Apr 16, 2025 at 4:04 PM Tom Lane <tgl@sss.pgh.pa.us> wrote: >>> Looking through all of the callers of freeJsonLexContext, quite >>> a lot of them use local JsonLexContext structs, and probably some >>> of them are more performance-critical than these. So that raises >>> the question of why are we seeing warnings for only these call >>> sites? > >> Yeah, I had the same question... > > After making another pass through the callers of freeJsonLexContext, > I observe that the warnings appear in callers that use a local > variable *and* contain goto statements. So I'm betting that the > presence of goto's causes the LTO optimizer to pull in its horns > quite a bit and thereby fail to detect the flag correlation. That seems plausible given the selective warnings. >>> Maybe there is a more elegant way to suppress them. > >> Can we brute-force ignore this particular warning site with a #pragma >> (suggested in [1])? > > That's surely not elegant :-(. However, I don't especially want to > rewrite away the goto's in these callers ... Agreed, moving to heap allocated structures for these callsites seem much better. Something like the attached should be enough I think? -- Daniel Gustafsson
Attachment
Daniel Gustafsson <daniel@yesql.se> writes: > Agreed, moving to heap allocated structures for these callsites seem much > better. Something like the attached should be enough I think? I confirm this silences those warnings on my Fedora 41 box. I'm content to do it like this, but maybe Jacob wants to investigate alternatives? regards, tom lane
On Thu, Apr 17, 2025 at 8:20 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: > I confirm this silences those warnings on my Fedora 41 box. Instead of doing lex = calloc(...); /* (error out on NULL return) */ makeJsonLexContextCstringLen(lex, ...); we need to do lex = makeJsonLexContextCstringLen(NULL, ...); /* (error out on NULL return) */ so that JSONLEX_FREE_STRUCT is set correctly. Otherwise we'll leak the main allocation: ==12550==ERROR: LeakSanitizer: detected memory leaks Direct leak of 120 byte(s) in 1 object(s) allocated from: #0 0xaaaae34d2a84 in __interceptor_calloc (/home/jacob/src/postgres/worktree-oauth/build-clang/src/interfaces/libpq/fuzz_libpq_handle_oauth_sasl_error+0x112a84) (BuildId: 359bf20b63a97771ccb3bd2c238485920485521f) #1 0xaaaae3510ff0 in handle_oauth_sasl_error /home/jacob/src/postgres/worktree-oauth/build-clang/../src/interfaces/libpq/fe-auth-oauth.c:511:8 > I'm content to do it like this, but maybe Jacob wants to > investigate alternatives? I was more worried about it when you said you wanted to get rid of the stack allocation API. (I like having the flexibility to choose between the two forms, not just for performance but also for struct embedding.) But I'm perfectly happy with just adjusting these sites. Thanks! --Jacob
> On 17 Apr 2025, at 17:48, Jacob Champion <jacob.champion@enterprisedb.com> wrote: > > On Thu, Apr 17, 2025 at 8:20 AM Tom Lane <tgl@sss.pgh.pa.us> wrote: >> I confirm this silences those warnings on my Fedora 41 box. > > Instead of doing > > lex = calloc(...); > /* (error out on NULL return) */ > makeJsonLexContextCstringLen(lex, ...); > > we need to do > > lex = makeJsonLexContextCstringLen(NULL, ...); > /* (error out on NULL return) */ > > so that JSONLEX_FREE_STRUCT is set correctly. Otherwise we'll leak the > main allocation: Since there is no way to determine if the allocation succeeded from outside of the JSON api it might be better to keep the calloc and explicitly free it? (Could a JsonLexContextBroken() function be useful perhaps?) -- Daniel Gustafsson