-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Take this program that uses strxfrm:
#include <assert.h>
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[2] = {'X', 'Z'};
int len = strxfrm(buf, "X", 1);
printf("len: %d, buf[0]: %c, buf[1]: %c\n", len, buf[0], buf[1]);
assert(buf[1] == 'Z');
return 0;
}I compiled this program with my x86_64's gcc and ran it: This leaves buf[0] and buf[1] as is.
$ gcc strxfrm.c -o strxfrm && ./strxfrm
len: 1, buf[0]: X, buf[1]: ZIf I compile libcmini, link my binary to it, and run it through hatari:
$ make # to build in build dir
$ LIBCMINI=$PWD/build/ m68k-atari-mint-gcc \
-nostdlib \
-I"$LIBCMINI" \
"$LIBCMINI/objs/crt0.o" \
strxfrm.c atexit_shim.c \
-L"$LIBCMINI" \
-lcmini \
-lgcc \
-o strxfrm_test.tos # my compiler told me _atexit was undefined, so the atexit_shim just defines it
$ hatari-prg-args -q \
--tos emutos-512k/etos512us.img \
--conout 2 \
-- "$(pwd)/strxfrm_test.tos"I got this in my terminal:
len: 1, buf[0]: X, buf[1]: .
strxfrm.c:11: Assertion buf[1] == 'Z' failed
So it looks like buf[1] has been replaced with the null terminator.
Looking at the code it makes sense:
// Since we called with char buf[2] = {'X', 'Z'}; + int len = strxfrm(buf, "X", 1);
size_t strxfrm(char *dest, const char *src, size_t n) {
if (n > 0) {
strncpy(dest, src, n); // since n is one, src is copied to dest, so dest is now 'X'.
dest[n] = '\0'; // then, dest[1] is overwritten with the null terminator, so dest is now '\0'.
n = strlen(dest); // the length of dest is returned. This should be strlen(src) I believe.
}
return n; // this should also return strlen(src) always
}If I slightly tweak the test I also get different results than my gcc
#include <assert.h>
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[2] = {'X', 'Z'};
int len = strxfrm(buf, "X", 0);
printf("len: %d, buf[0]: %c, buf[1]: %c\n", len, buf[0], buf[1]);
return 0;
}Compiled with my host gcc prints:
len: 1, buf[0]: X, buf[1]: Zthe return value should always be the strlen of src, even if nothing happens (n is 0). Note that buf's len is 2, src's len is 1, and n is 0, so it is returning strlen(src) here.
libcmini's version prints 0, because the implementation returns 0 if n is 0, otherwise strlen(dest), instead of strlen(src).
len: 0, buf[0]: X, buf[1]: ZSo the fixes:
- Always return strlen(src).
- If there's space in dest, stick a null terminator. That only happens if n > strlen(dest) + 1. Otherwise, drop the null terminator and copy away.
I assume something like this would work and line up with what I see on gcc.
size_t strxfrm(char *dest, const char *src, size_t n) {
size_t len = strlen(src);
if (n > len) {
memcpy(dest, src, len + 1);
}
return len;
}