| Summary: | va_list assignment in printfExtend__I is unsafe for 64-bit Linux targets | ||
|---|---|---|---|
| Product: | [Technology] RTSC | Reporter: | Sasha Slijepcevic <sascha> |
| Component: | Runtime | Assignee: | Dave Russo <d-russo> |
| Status: | CLOSED FIXED | QA Contact: | |
| Severity: | normal | ||
| Priority: | P3 | CC: | cring, d-russo, dfriedland |
| Version: | unspecified | ||
| Target Milestone: | 3.30.2 | ||
| Hardware: | All | ||
| OS: | All | ||
| Whiteboard: | target:3.30.02 | ||
| Bug Depends on: | |||
| Bug Blocks: | 436105 | ||
|
Description
Sasha Slijepcevic
It appears that 64-bit Linux targets require more than a simple pointer for va_list. So, in the interest of performance, a va_list is often typedef'd to be an array consisting of a single element rather than a simple pointer. Making it an array means that when passing a va_list to a function, only the address of the first element array is passed _not_ the array itself.
This also has the consequence that any function that's passed a va_list _must never_ take the address of this parameter thinking that they have a pointer to a va_list. For example, the following code is broken
void doPrint(char *fmt, va_list *pva)
{
/* use *pva as a va_list */
... va_arg(*pva, ...);
}
void vprint(char *fmt, va_list va)
{
/* BUG: if va_list is an array type, &va doesn't point to a va_list!! */
doPrint(fmt, &va);
}
For a nice discussion of the issue see comments 10-14 of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=14557.
The accepted solution to the issue is to use the C99 macro va_copy() to initialize a new va_list, pass a pointer to the initialized va_list, _and_ call va_end() to finalize use of the new va_list. For example, a portable vprint() can be written as follows:
void vprint(char *fmt, va_list va)
{
va_list nva;
va_copy(nva, va);
doPrint(fmt, &nva);
va_end(nva);
}
However, if the reason for defining a doPrint() to take a va_list* is to enable it to modify the original va_list, there is no need to take it's address in the first place! The code above can be written as just
void doPrint(char *fmt, va_list va)
{
/* updates callers va list!!!! */
... va_arg(va, ...);
}
void vprint(char *fmt, va_list va)
{
doPrint(fmt, va);
}
Notice that the type signature of doPrint() has to change.
fixed in xdc-A41 (to be included in xdctools 3.30.02) First I replicated the problem with the target Linux86_64 by adding platformprod-t42 to the package path. I also used 4.4.6 Gnu compiler configured with 64-bit support. With that setup, XDCtools 3.30.01 reports the following error when building an example: package/cfg/Mod_p86U.c: In function ‘xdc_runtime_System_printfExtend__I’: package/cfg/Mod_p86U.c:766: error: invalid initializer package/cfg/Mod_p86U.c:846: error: incompatible types when assigning to type ‘VaList’ from type ‘struct __va_list_tag *’ package/cfg/Mod_p86U.c:859: error: incompatible types when assigning to type ‘va_list’ from type ‘struct __va_list_tag *’ package/cfg/Mod_p86U.c:900: error: incompatible types when assigning to type ‘VaList’ from type ‘struct __va_list_tag *’ After switching to 3.30.02.44, with the same target and the same example, the build completes without errors. - shipped in XDCtools 3.30.02.44 |