Skip to content

Fix #678: Fix stats print on ARM due to indeterminate state of "va_list" parameter after vasprintf#933

Closed
IgnoreWarnings wants to merge 2 commits into
masterfrom
bug-print
Closed

Fix #678: Fix stats print on ARM due to indeterminate state of "va_list" parameter after vasprintf#933
IgnoreWarnings wants to merge 2 commits into
masterfrom
bug-print

Conversation

@IgnoreWarnings
Copy link
Copy Markdown
Collaborator

@IgnoreWarnings IgnoreWarnings commented Jun 26, 2025

The state of "va_list" parameter is indeterminant after a call to vasprintf, which causes UB on ARM.
For detailed error explanation: #678.

  1. Portability: Specifiying the "va" parameter as reference to ensure reproducable behavior on arm and x86.
  2. "vstrcatf" now copies the va_list parameter to prevent it beeing undetermined after call to vasprintf.

@stv0g
I made the decision to put the responsibility of advancing the list in the called function rather than the caller because this is how the existing code in table.cpp:void Table::row(int count, ...) assumes it should work.

@fwege

@IgnoreWarnings IgnoreWarnings changed the title Fix #678: Fix stats print on ARM due to indeetermined state of va_list parameter after vasprintf Fix #678: Fix stats print on ARM due to indetermined state of va_list parameter after vasprintf Jun 26, 2025
@IgnoreWarnings IgnoreWarnings changed the title Fix #678: Fix stats print on ARM due to indetermined state of va_list parameter after vasprintf Fix #678: Fix stats print on ARM due to indeterminate state of va_list parameter after vasprintf Jun 26, 2025
@IgnoreWarnings IgnoreWarnings changed the title Fix #678: Fix stats print on ARM due to indeterminate state of va_list parameter after vasprintf Fix #678: Fix stats print on ARM due to indeterminate state of "va_list" parameter after vasprintf Jun 26, 2025
Specifiying the "va" parameter as reference to ensure reproducable behavior on arm and x86.

Signed-off-by: Pascal Bauer <pascal.bauer@rwth-aachen.de>
vstrcatf now copies the va_list parameter to prevent it beeing undetermined after call to vasprintf. See full desciption in #678.

Signed-off-by: Pascal Bauer <pascal.bauer@rwth-aachen.de>
@IgnoreWarnings IgnoreWarnings added the bug Something isn't working label Jun 26, 2025
@IgnoreWarnings IgnoreWarnings marked this pull request as ready for review June 26, 2025 12:14
Copy link
Copy Markdown
Member

@n-eiling n-eiling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, some minor comments below.

Comment thread common/lib/utils.cpp

char *vstrcatf(char **dest, const char *fmt, va_list ap) {
char *vstrcatf(char **dest, const char *fmt, va_list &ap) {
char *tmp;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add null pointer checks for dest and fmt

Comment thread common/lib/utils.cpp
double randf() { return (double)random() / RAND_MAX; }

char *vstrcatf(char **dest, const char *fmt, va_list ap) {
char *vstrcatf(char **dest, const char *fmt, va_list &ap) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised you don't need const char* restrict fmt.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my knowledge restrict is a C not C++ keyword?

Are you concerned about aliasing between the *dest and fmt pointers?

I dont fully understand, why we might need it..

Comment thread common/lib/utils.cpp
va_arg(ap, double);
} else if (strcmp(format, "%s") == 0) {
va_arg(ap, char *);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is real-time critical code. But you could avoid one allocation by doing:

size_t n = vsnprintf(NULL, 0, fmt, va_copy);
*dest = (char *)(realloc(*dest, n + i + 1));
vsnprintf(*dest + n, n+1, fmt, va_copy);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering, isnt it also UB to call vsnprintf again with the same va_copy?

@IgnoreWarnings wrote in #678

This bug occures because "va_list" is used after beeing passed to "vasprintf", which leads to undeterministic behavior.
Functions like "vasprintf" "invoke va_arg at least once, the value of arg is indeterminate after the return." (https://en.cppreference.com/w/c/io/vfprintf : Notes).

@stv0g
Copy link
Copy Markdown
Contributor

stv0g commented Jun 27, 2025

Hi @IgnoreWarnings,

I think a found a nicer solution to the issue in #935.

What do you think about it? Thanks for the analysis. I definitely learned something new :)

@IgnoreWarnings
Copy link
Copy Markdown
Collaborator Author

IgnoreWarnings commented Jun 27, 2025

Hi @IgnoreWarnings,

I think a found a nicer solution to the issue in #935.

What do you think about it? Thanks for the analysis. I definitely learned something new :)

Feel free to solve this issue however you like.
I personally would have reimplemented without using va_lists at all with modern features like std::format instead of this whole manual allocation. But that would be quiet some work to do.

@IgnoreWarnings
Copy link
Copy Markdown
Collaborator Author

IgnoreWarnings commented Jun 27, 2025

Moved to #935 .

@stv0g stv0g deleted the bug-print branch August 21, 2025 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants