Substitute or workaround for asprintf on AIX - c

I'm trying to build python-kerberos on AIX. kerberospw.c uses a call to asprintf, but from what Google is telling me, asprintf does not exist on AIX.
I saw http://www.koders.com/c/fidAA9B130D588302673A28B568430A83131B7734C0.aspx?s=windows.h, which looks like I could create a stand-in asprintf, but I don't know where this would go or how I would #include it in kerberospw.c.
Is there a way I can use the koders.com example or some other code to "fake" asprintf? Can I just include the asprintf function as shown in kerberospw.c? I am not a C coder, but
asprintf (char **resultp, const char *format, ...)
doesn't look like a valid signature to me with the dots at the end. The relevant line from kerberospw.c is below
asprintf(&message, "%.*s: %.*s",
(int) result_code_string.length,
(char *) result_code_string.data,
(int) result_string.length,
(char *) result_string.data);
I realize I could contact the author of python-kerberos, but a) I think it would be helpful to have a potential patch if I did so, and b) there might be other software I run across that uses asprintf, and it would be nice to have a workaround.

The asprintf is a variation of the printf family of function that allocate a buffer to hold the memory for the formatted string and return it. It is a function with a variable number of argument (hence the ... in the declaration that is valid C code). You can find a description here.
It can be reimplemented relatively easily if the vsnprintf is functioning correctly (ie, return an error if the buffer is too small to hold the formatted string).
Here is such an implementation:
#include <stdarg.h>
int asprintf(char **ret, const char *format, ...)
{
va_list ap;
*ret = NULL; /* Ensure value can be passed to free() */
va_start(ap, format);
int count = vsnprintf(NULL, 0, format, ap);
va_end(ap);
if (count >= 0)
{
char* buffer = malloc(count + 1);
if (buffer == NULL)
return -1;
va_start(ap, format);
count = vsnprintf(buffer, count + 1, format, ap);
va_end(ap);
if (count < 0)
{
free(buffer);
return count;
}
*ret = buffer;
}
return count;
}

Building on Sylvain's answer, here is a simple implementation with both asprintf() and vasprintf() because where you need one, you usually end up needing the other too. And, given the va_copy() macro from C99, it is easy to implement asprintf() in terms of vasprintf(). Indeed, when writing varargs functions, it is very often helpful to have them in pairs, one with the ellipsis notation and one with the va_list argument in place of the ellipsis, and you trivially implement the former in terms of the latter.
This leads to the code:
int vasprintf(char **ret, const char *format, va_list args)
{
va_list copy;
va_copy(copy, args);
/* Make sure it is determinate, despite manuals indicating otherwise */
*ret = NULL;
int count = vsnprintf(NULL, 0, format, args);
if (count >= 0)
{
char *buffer = malloc(count + 1);
if (buffer == NULL)
count = -1;
else if ((count = vsnprintf(buffer, count + 1, format, copy)) < 0)
free(buffer);
else
*ret = buffer;
}
va_end(copy); // Each va_start() or va_copy() needs a va_end()
return count;
}
int asprintf(char **ret, const char *format, ...)
{
va_list args;
va_start(args, format);
int count = vasprintf(ret, format, args);
va_end(args);
return(count);
}
The tricky part of using these functions in a system where they are not provided is deciding where the functions should be declared. Ideally, they'd be in <stdio.h>, but then you wouldn't need to write them. So, you have to have some other header which includes <stdio.h> but declares these functions if they are not declared in <stdio.h>. And, ideally, the code should semi-automatically detect this. Maybe the header is "missing.h", and contains (in part):
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdarg.h>
#ifndef HAVE_ASPRINTF
extern int asprintf(char **ret, const char *format, ...);
extern int vasprintf(char **ret, const char *format, va_list args);
#endif /* HAVE_ASPRINTF */
Also, note that this man page for asprintf() says that the return value in the pointer is indeterminate in case of error. Other man pages, including the one referenced in the question, indicate that it is explicitly set to NULL on error. The C Standard committee document (n1337.pdf) does not specify the error behaviour on lack of memory.
If using asprintf(), do not assume that the pointer is initialized if the function fails.
If implementing asprintf(), ensure that the pointer is set to null on error to give deterministic behaviour.

I came here looking for a quick implementation for Windows and Linux which set the return pointer to NULL on failure.
Jonathan Leffler's answer looked to be the better one, but then I noticed it doesn't set -1 when malloc fails.
I did more searching and came across this discussion of implementing asprintf, which then enlightened me that Jonathan and Sylvain both did not handle overflow correctly either.
I now recommend this solution provided with the aforementioned discussion, which seems to be cover all the important platforms and apparently handles every failure scenario correctly.

Here an implementation that doesn't call snprintf() twice in most of the cases. I omitted the includes and defines as shown in other responses.
As it should be, define the asprintf() as a call to vasprintf()
int asprintf(char **dst, const char * pcFormat, ...)
{
va_list ap;
va_start(ap, pcFormat);
int len = vasprintf(dst, pcFormat, ap);
va_end(ap);
return len;
}
We preallocate a buffer to an predefined appropriate size and only in case of overflow call vsnprintf() a second time. The rationale being that s*printf() function are considered very heavy and overallocating memory being acceptable.
int vasprintf(char **dst, const char * pcFormat, va_list ap)
{
int len = 512; /* Worked quite well on our project */
int allocated = 0;
va_list ap_copy;
char *buff = NULL;
while(len >= allocated) {
free(buff);
buff = malloc(len+1);
if(buff) {
allocated = len+1;
va_copy(ap_copy, ap);
len = vsnprintf(buff, len+1, pcFormat, ap_copy);
va_end(ap_copy);
}
else /* malloc() failed */
return -1;
}
*dst = buff;
return len;
}
EDIT: I replaced the realloc() call by a simple malloc() as it is cheaper. In the case of overflow a free()/malloc() pair costs less than realloc() because of its internal hidden memcpy(). As we overwrite the whole buffer anyway with the subsequent call to vsnprintf() there is no point in that copy.

Related

Dynamically allocate `char*` with concatention and format literals?

With support for macOS, Windows (MSVC), and Linux, how do I do the following?
char *s;
func(&s, "foo");
if (<condition>) func(&s, "bar%s", "can")
/* want "foobarcan", and I don't know `strlen(s)` AoT */
I've tried with asprintf (was able to find an MSVC implementation) but that didn't seem to work well on this kind of workflow. fopencookie and funopen seem convenient but unavailable on MSVC.
Maybe there's some clean way with realloc to create a NUL ended char* in C?
As pointed out in the comments, (v)snprintf always returns the number of bytes that would have been written (excluding the null terminating byte), even if truncated. This has the effect that providing the function with a size argument of 0 returns the length of the to-be-formatted string.
Using this value, plus the string length of our existing string (if applicable), plus one, we (re)allocate the appropriate amount of memory.
To concatenate, simply print the formatted string at the correct offset.
An example, sans error checking.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *dstr(char **unto, const char *fmt, ...) {
va_list args;
size_t base_length = unto && *unto ? strlen(*unto) : 0;
va_start(args, fmt);
/* check length for failure */
int length = vsnprintf(NULL, 0, fmt, args);
va_end(args);
/* check result for failure */
char *result = realloc(unto ? *unto : NULL, base_length + length + 1);
va_start(args, fmt);
/* check for failure*/
vsprintf(result + base_length, fmt, args);
va_end(args);
if (unto)
*unto = result;
return result;
}
int main(void) {
char *s = dstr(NULL, "foo");
dstr(&s, "bar%s%d", "can", 7);
printf("[[%s]]\n", s);
free(s);
}
stdout:
[[foobarcan7]]
The caveat here is that you can not write:
char *s;
dstr(&s, "foo");
s must be initialized as NULL, or the function must be used directly as an initializer, with the first argument set to NULL.
That, and the second argument is always treated as a format string. Use other means of preallocating the first string if it contains unsanitary data.
Example exploit:
/* exploit */
char buf[128];
fgets(buf, sizeof buf, stdin);
char *str = dstr(NULL, buf);
puts(str);
free(str);
stdin:
%d%s%s%s%s%d%p%dpapdpasd%d%.2f%p%d
Result: Undefined Behavior

C - Variable Argument with Sprintf?

I have a function;
void foo(const char* format, ...)
{
char buffer[1080];
// Supposed way to handle C Variable Arguments?
va_list argptr;
va_start(argptr, format);
sprintf(buffer, format, argptr);
va_end(argptr);
printf_s("%s.\n", buffer);
}
int main()
{
int val = 53;
foo("%d", val);
}
Everytime I run this, I get MASSIVE numbers that change every during each run. 12253360, 5306452, etc. I don't understand why.
Is it something with my sprintf call or is it the way I'm doing the va_list argptr;? Is my buffer too large?
Thanks.
The technique you are apparently attempting to use suggests that you need vsprintf (or, better, vsnprintf)
va_list argptr;
va_start(argptr, format);
vsnprintf(buffer, sizeof buffer, format, argptr);
va_end(argptr);
That's the very reason such functions from v... group exist in standard library.
Calling sprintf the way you do it makes no sense at all - it cannot be used with an externally supplied va_list.
If you want to implement a variable-sized buffer you can do it as follows
void foo(const char* format, ...)
{
static char *buffer;
static size_t buffer_size;
va_list argptr;
va_start(argptr, format);
int length = vsnprintf(buffer, buffer_size, format, argptr);
va_end(argptr);
if (length + 1 > buffer_size)
{
buffer_size = length + 1;
buffer = realloc(buffer, buffer_size);
/* Yes, `realloc` should be done differently to properly handle
possible failures. But that's beside the point in this context */
va_start(argptr, format);
vsnprintf(buffer, buffer_size, format, argptr);
va_end(argptr);
}
printf("%s.\n", buffer);
}
You can, of course, change the memory management strategy to something different, like use a fixed local buffer of 512 bytes in the first call, and then use a temporary dynamically allocated buffer in the second call only if 512 proves to be insufficient. And so on...

C variadic wrapper

To output formatted debug output, I've written a wrapper for vsfprint. Now, I wanted to allocate exactly enough memory for the output buffer, instead of just claiming a random high buffer size (it's a small embedded platform (ESP8266)). For that I iterate through the variable arguments until a NULL is found.
This works fine, provided that I don't forget to add a (char *)NULL parameter to every call. So, I thought, let create another wrapper, a function that just relays all arguments and adds a (char *) NULL parameter:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // malloc
void write_log(const char *format, ...) {
char* buffdyn;
va_list args;
// CALC. MEMORY
size_t len;
char *p;
if(format == NULL)
return;
len = strlen(format);
va_start(args, format);
while((p = va_arg(args, char *)) != NULL)
len += strlen(p);
va_end(args);
// END CALC. MEMORY
// ALLOCATE MEMORY
buffdyn = malloc(len + 1); /* +1 for trailing \0 */
if(buffdyn == NULL) {
printf("Not enough memory to process message.");
return;
}
va_start(args, format);
//vsnprintf = Write formatted data from variable argument list to sized buffer
vsnprintf(buffdyn, len, format, args);
va_end(args);
printf("%s\r\n",buffdyn);
free(buffdyn);
}
void write_log_wrapper(const char *format, ...) {
va_list arg;
va_start(arg, format);
write_log(format,arg,(char *)NULL);
va_end(arg);
}
int main()
{
const char* sDeviceName = "TEST123";
const char* sFiller1 = "12345678";
write_log_wrapper("Welcome to %s%s", sDeviceName,sFiller1);
write_log("Welcome to %s%s", sDeviceName,sFiller1, (char *)NULL);
return 0;
}
Calling the write_log() function directly works fine (if you don't forget the NULL parameter). Calling the write_log_wrapper() function will only display the first paramter, and then adds a "(nu" (garbage?) to the output.
What am I doing wrong? Is this a good way to approach what I'm aiming to do in the first place?
Thanks.
To determine how big a buffer is needed to hold the output string, you need to fully parse the entire format string and actually expand the arguments.
You can either do it yourself, duplicating all the processing of printf() and its ilk and hoping to not make any mistakes, or you can use vsnprintf() - first to determine the size, and then to actually expand the inputs to one output string.
#define FIXED_SIZE 64
void write_log(const char *format, ...)
{
// set up a fixed-size buffer and a pointer to it
char fixedSizeBuffer[ FIXED_SIZE ];
char *outputBuffer = fixedSizeBuffer;
// no dynamic buffer yet
char *dynamicBuffer = NULL;
// get the variable args
va_list args1;
va_start( args1, format );
// need to copy the args even though we won't know if we
// need them until after we use the first set
va_list args2;
va_copy( args2, args1 );
// have to call vsnprintf at least once - might as well use a small
// fixed-size buffer just in case the final string fits in it
int len = vsnprintf( fixedSizeBuffer, sizeof( fixedSizeBuffer ), format, args1 );
va_end( args1 );
// it didn't fit - get a dynamic buffer, expand the string, and
// point the outputBuffer pointer at the dynamic buffer so later
// processing uses the right string
if ( len > sizeof( fixedSizeBuffer ) )
{
dynamicBuffer = malloc( len + 1 );
vsnprintf( dynamicBuffer, len + 1, format, args2 );
outputBuffer = dynamicBuffer;
}
va_end( args2 );
// do something with outputBuffer
free( dynamicBuffer );
return;
}
What am I doing wrong?
Passing a va_list arg
write_log(format, arg, (char *)NULL);
is not the same as passing several char*
write_log("Welcome to %s%s", sDeviceName, sFiller1, (char *)NULL);
You won't get around to pass a sentinel marking the end of the parameters passed, that is a (char*) NULL or whatever you decide to use.
Alternatives would be to
pass the number of arguments explicitly, perhaps as 2nd parameter
parse the format string for conversion specifiers, in fact mimicking what printf does.
If you want just to ensure that all calls receive a setinel at the end, use a macro:
#define WRITE_LOG(...) write_log(__VA_ARGS__, (char*)0)
This ensures that there is always an extra 0 at the end.
Also be careful with NULL. It is underspecified in the C standard to what expressions this resolves. Common cases are 0 and (void*)0. So on 64bit architectures these may have different width (32 bit for the first, 64 bit for the second). It can be deadly for a variadic function to receive the wrong width here. Therefore I used (char*)0 which is the type that your function seems to expect. (But (void*)0 would also do in this special case.)

possible buffer overflow vulnerability for va_list in C?

I have the following code:
int ircsocket_print(char *message, ...)
{
char buffer[512];
int iError;
va_list va;
va_start(va, message);
vsprintf(buffer, message, va);
va_end(va);
send(ircsocket_connection, buffer, strlen(buffer), 0);
return 1;
}
And I wanted to know if this code is vulerable to buffer overflows by providing char arrays with a size > 512 to the variables list? And if so - How can I fix this?
thank you.
Yes, it is vulnerable.
You can implement your function this way:
int ircsocket_print(char *message, ...)
{
char buf[512];
char *buffer;
int len;
va_list va;
buffer = buf;
va_start(va, message);
len = vsnprintf(buffer, 512, message, va);
va_end(va);
if (len >= 512)
{
buffer = (char*)malloc(len + 1);
va_start(va, message);
len = vsnprintf(buffer, len + 1, message, va);
va_end(va);
}
send(ircsocket_connection, buffer, len, 0);
if (buffer != buf)
free(buffer);
return 1;
}
Yes, it is vulnerable.
Simply use vsnprintf instead:
vsnprintf(buffer, sizeof(buffer), message, va);
As everyone else noted, the basic answer to your question is "Yes, you are vulnerable to buffer overflows".
In C99, you could use a VLA:
void ircsocket_print(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = vsnprintf(0, 0, message, args);
va_end(args);
char buffer[len+1];
va_start(args, fmt);
int len = vsnprintf(buffer, len+1, message, args);
va_end(args);
send(ircsocket_connection, buffer, len, 0);
}
Note the idiom that calls vsnprintf() once with length 0 (the second zero) to obtain the length required, then calls it a second time to format the data into the buffer. Also note the careful resetting of args after each call to vsnprintf(); that is required by the C standard:
ยง7.15 <stdarg.h>
If access to the varying arguments is
desired, the called function shall declare an object (generally referred to as ap in this
subclause) having type va_list. The object ap may be passed as an argument to
another function; if that function invokes the va_arg macro with parameter ap, the
value of ap in the calling function is indeterminate and shall be passed to the va_end
macro prior to any further reference to ap.
One downside to this formulation is that it takes a pessimistic view and unconditionally calls vsnprintf() twice. You might prefer to take an optimistic view (most of the time, 512 will be enough), and only allocate more space if the first call shows that it is insufficient.
One more downside to the use of a VLA like this is that if you run out of space for the local variable buffer, your code may never get a chance to recover. You must judge how serious a problem that is. If it is an issue, use explicit memory allocation (malloc()) instead:
char buffer = malloc(len+1);
if (buffer == 0)
return; // Report error?
...second vsnprintf() and send()...
free(buffer);
Since your function only ever returned the constant 1, there is no obvious reason to make it a function returning anything - if it is a function returning void, the calling code does not need any check on the returned value. OTOH, maybe you should be returning the result of the send() call (and possibly an error indication if the malloc() fails).
I also made the format (message) parameter into a const char *; neither this function nor the ones it calls modify the format string.
If you're on Linux you could use vasprintf() which will allocate a buffer of the correct size.
If you need portability you can use vsnprintf() to avoid buffer overflows.

create a my_printf that sends data to both a sprintf and the normal printf?

I am playing with the printf and the idea to
write a my_printf(...) that calls the normal printf
and a sprintf that sends the result to a special function.
(I was thinking about sprintf since that behaves just like printf on most platforms).
My idea was to write a small macro that did this:
#define my_printf(X, Y...) do{ printf(X, ## Y); \
char* data = malloc(strlen(X)*sizeof(char)); \
sprintf(data, X, ## Y); \
other_print(data);\
free(data);}while(0)
But since sprintf can expand the string to a much bigger size than X,
this method breaks almost directly.
And just to add a number do the malloc seems to be the wrong way to attack the problem,
since then I would just move the problem into the future and a day when I want print a big expression...
Does anyone has a better idea on how to attack this problem?
Or how do I know how big the sprintf result will be?
Thanks
Johan
Update: I forgot that printf returns how many chars it prints,
and since I already is calling printf in the macro it was a very easy thing to add
a int that saves the number.
#define buf_printf(X, Y...) do{ int len = printf(X, ## Y); \
char* data = malloc((len+1)*sizeof(char)); \
sprintf(data, X, ## Y); \
other_print(data);\
free(data);}while(0)
Update: I was thinking about this and maybe to use a normal function
that looks a lot like what ephemient has suggested is a good idea.
The key there seems to be the v-version of the different printf functions
(vprintf, vsprintf and vsnprintf). Thanks for pointing that out.
Thanks again
Johan
Use snprintf to calculate the size. From the man page:
" If the output was truncated due to this limit then the return value is the number of characters (not including the trailing '\0') which would have been written to the final string if enough space had been available "
snprintf is standard from C99. If you only have a C89 compiler then check the documentation: pre-standard versions might not return the value you want. Again according to the man page, glibc prior to version 2.1 used to return -1 if the output was truncated, not the required size.
By the way, sizeof(char) is defined to be 1, always, in every C implementation ever :-)
The best way to do this is with varargs. Create a function with the same prototype as printf() and use the varargs functions to pass data to sprintf to populate the desired buffer, the also pass that buffer to printf("%s") before returning.
A lot of the early implementations had a 4K limit on the lowest level printf() call but I would opt for more than that. You probably need to just set an upper limit and stick to it.
One trick we used in a logging system was to write the data using printf() to a /dev/null handle. Since printf() returns the number of characters written, we then used that to allocate a buffer. But that wasn't very efficient since it involved calling a printf()-type function twice.
Since you are on Linux I'd suggest to use asprintf() - it it GNU extension that allocates string for you.
And thanks to C99 variadic macros you don't need to mess with varagrs.
So your macro would look like:
#define MY_PRINT(...) do { \
char *data; \
asprintf(&data, __VA_ARGS__); \
printf("%s", data); \
other_print(data); \
free(data); \
} while (0)
NB! This is C99 and GNU-only code
Edit: Now it will evaluate macro arguments just once so calling macro with something like ("%d", i++) will work correctly.
If you're always running on a system with glibc (i.e. Linux and any other OS with GNU userland), asprintf acts just like sprintf but can automatically handle allocation itself.
int my_printf(const char *fmt, ...) {
char *buf = NULL;
int len;
va_list ap;
va_start(ap, &fmt);
len = vasprintf(&buf, fmt, ap);
va_end(ap);
if (len < 0) {
/* error: allocation failed */
return len;
}
puts(buf);
other_print(buf);
free(buf);
return len;
}
Pax's answer is more portable, but instead of printing to /dev/null, here's a better trick: by POSIX, snprintf can be given a NULL buffer and 0 size, and it'll return how much it would have written -- but obviously it won't actually write anything.
int my_printf(const char *fmt, ...) {
char *buf;
int len, len2;
va_list ap;
va_start(ap, &fmt);
len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
buf = malloc(len + 1);
if (!buf) {
/* error: allocation failed */
return -1;
}
va_start(ap, &fmt);
len2 = snprintf(buf, len + 1, fmt, ap);
buf[len] = '\0';
va_end(ap);
/* has another thread been messing with our arguments?
oh well, nothing we can do about it */
assert(len == len2);
puts(buf);
other_printf(buf);
free(buf);
return len;
}
Well, as onebyone says, some old systems don't have a compliant snprintf. Can't win them all...

Resources