mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-23 10:28:05 +00:00
This is a confusing change as it seems the original code was just wrong. GCC 12 complains with: In function ‘__conv_val’, inlined from ‘std_strtoul’ at compel/plugins/std/string.c:202:7: compel/plugins/std/string.c:154:24: error: array subscript 97 is above array bounds of ‘const char[37]’ [-Werror=array-bounds] 154 | return &conv_tab[__tolower(c)] - conv_tab; | ^~~~~~~~~~~~~~~~~~~~~~~ compel/plugins/std/string.c: In function ‘std_strtoul’: compel/plugins/std/string.c:10:19: note: while referencing ‘conv_tab’ 10 | static const char conv_tab[] = "0123456789abcdefghijklmnopqrstuvwxyz"; | ^~~~~~~~ cc1: all warnings being treated as errors Which sounds correct. The array conv_tab has just 37 elements. If I understand the code correctly we are trying to convert anything that is character between a-z and A-Z to a number for cases where the base is larger than 10. For a base 11 conversion b|B should return 11. For a base 35 conversion z|Z should return 35. This is all for a strtoul() implementation. The original code was: static const char conv_tab[] = "0123456789abcdefghijklmnopqrstuvwxyz"; return &conv_tab[__tolower(c)] - conv_tab; and that seems wrong. If conv_tab would have been some kind of hash it could have worked, but '__tolower()' will always return something larger than 97 ('a') which will always overflow the array. But maybe I just don't get that part of the code. I replaced it with return __tolower(c) - 'a' + 10; which does the right thing: 'A' = 10, 'B' = 11 ... 'Z' = 35 Signed-off-by: Adrian Reber <areber@redhat.com>
297 lines
5.0 KiB
C
297 lines
5.0 KiB
C
#include <sys/types.h>
|
|
#include <stdbool.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <compel/plugins/std/syscall.h>
|
|
#include <compel/plugins/std/string.h>
|
|
|
|
#include "features.h"
|
|
|
|
static const char conv_tab[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
void std_dputc(int fd, char c)
|
|
{
|
|
sys_write(fd, &c, 1);
|
|
}
|
|
|
|
void std_dputs(int fd, const char *s)
|
|
{
|
|
for (; *s; s++)
|
|
std_dputc(fd, *s);
|
|
}
|
|
|
|
static size_t __std_vprint_long_hex(char *buf, size_t blen, unsigned long num, char **ps)
|
|
{
|
|
char *s = &buf[blen - 2];
|
|
|
|
buf[blen - 1] = '\0';
|
|
|
|
if (num == 0) {
|
|
*s = '0', s--;
|
|
goto done;
|
|
}
|
|
|
|
while (num > 0) {
|
|
*s = conv_tab[num % 16], s--;
|
|
num /= 16;
|
|
}
|
|
|
|
done:
|
|
s++;
|
|
*ps = s;
|
|
return blen - (s - buf);
|
|
}
|
|
|
|
static size_t __std_vprint_long(char *buf, size_t blen, long num, char **ps)
|
|
{
|
|
char *s = &buf[blen - 2];
|
|
int neg = 0;
|
|
|
|
buf[blen - 1] = '\0';
|
|
|
|
if (num < 0) {
|
|
neg = 1;
|
|
num = -num;
|
|
} else if (num == 0) {
|
|
*s = '0';
|
|
s--;
|
|
goto done;
|
|
}
|
|
|
|
while (num > 0) {
|
|
*s = (num % 10) + '0';
|
|
s--;
|
|
num /= 10;
|
|
}
|
|
|
|
if (neg) {
|
|
*s = '-';
|
|
s--;
|
|
}
|
|
done:
|
|
s++;
|
|
*ps = s;
|
|
return blen - (s - buf);
|
|
}
|
|
|
|
void std_vdprintf(int fd, const char *format, va_list args)
|
|
{
|
|
const char *s = format;
|
|
|
|
for (; *s != '\0'; s++) {
|
|
char buf[32], *t;
|
|
int along = 0;
|
|
|
|
if (*s != '%') {
|
|
std_dputc(fd, *s);
|
|
continue;
|
|
}
|
|
|
|
s++;
|
|
if (*s == 'l') {
|
|
along = 1;
|
|
s++;
|
|
if (*s == 'l')
|
|
s++;
|
|
}
|
|
|
|
switch (*s) {
|
|
case 's':
|
|
std_dputs(fd, va_arg(args, char *));
|
|
break;
|
|
case 'd':
|
|
__std_vprint_long(buf, sizeof(buf), along ? va_arg(args, long) : (long)va_arg(args, int), &t);
|
|
std_dputs(fd, t);
|
|
break;
|
|
case 'x':
|
|
__std_vprint_long_hex(buf, sizeof(buf), along ? va_arg(args, long) : (long)va_arg(args, int),
|
|
&t);
|
|
std_dputs(fd, t);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void std_dprintf(int fd, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
std_vdprintf(fd, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
static inline bool __isspace(unsigned char c)
|
|
{
|
|
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
|
|
}
|
|
|
|
static unsigned char __tolower(unsigned char c)
|
|
{
|
|
return (c <= 'Z' && c >= 'A') ? c - 'A' + 'a' : c;
|
|
}
|
|
|
|
static inline bool __isalpha(unsigned char c)
|
|
{
|
|
return ((c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a'));
|
|
}
|
|
|
|
static inline bool __isdigit(unsigned char c)
|
|
{
|
|
return (c <= '9' && c >= '0');
|
|
}
|
|
|
|
static inline bool __isalnum(unsigned char c)
|
|
{
|
|
return (__isalpha(c) || __isdigit(c));
|
|
}
|
|
|
|
static unsigned int __conv_val(unsigned char c)
|
|
{
|
|
if (__isdigit(c))
|
|
return c - '0';
|
|
else if (__isalpha(c))
|
|
/**
|
|
* If we want the value of something which __isalpha() == true
|
|
* it has to be base > 10. 'A' = 10, 'B' = 11 ... 'Z' = 35
|
|
*/
|
|
return __tolower(c) - 'a' + 10;
|
|
|
|
return -1u;
|
|
}
|
|
|
|
unsigned long std_strtoul(const char *nptr, char **endptr, int base)
|
|
{
|
|
const char *s = nptr;
|
|
bool neg = false;
|
|
unsigned int v;
|
|
long num = 0;
|
|
|
|
if (base < 0 || base == 1 || base > 36)
|
|
goto fin;
|
|
|
|
while (__isspace(*s))
|
|
s++;
|
|
if (!*s)
|
|
goto fin;
|
|
|
|
if (*s == '-')
|
|
neg = true, s++;
|
|
|
|
if (base == 0) {
|
|
if (s[0] == '0') {
|
|
unsigned char p = __tolower(s[1]);
|
|
switch (p) {
|
|
case 'b':
|
|
base = 2, s += 2;
|
|
break;
|
|
case 'x':
|
|
base = 16, s += 2;
|
|
break;
|
|
default:
|
|
base = 8, s += 1;
|
|
break;
|
|
}
|
|
} else
|
|
base = 10;
|
|
} else if (base == 16) {
|
|
if (s[0] == '0' && __tolower(s[1]) == 'x')
|
|
s += 2;
|
|
}
|
|
|
|
for (; *s; s++) {
|
|
if (__isspace(*s))
|
|
continue;
|
|
if (!__isalnum(*s))
|
|
goto fin;
|
|
v = __conv_val(*s);
|
|
if (v == -1u || v > base)
|
|
goto fin;
|
|
num *= base;
|
|
num += v;
|
|
}
|
|
|
|
fin:
|
|
if (endptr)
|
|
*endptr = (char *)s;
|
|
return neg ? (unsigned long)-num : (unsigned long)num;
|
|
}
|
|
|
|
/*
|
|
* C compiler is free to insert implicit calls to memcmp, memset,
|
|
* memcpy and memmove, assuming they are available during linking.
|
|
* As the parasite code is not linked with libc, it must provide
|
|
* our own implementations of the above functions.
|
|
* Surely, these functions can also be called explicitly.
|
|
*
|
|
* Note: for now, not having memmove() seems OK for both gcc and clang.
|
|
*/
|
|
|
|
#ifndef ARCH_HAS_MEMCPY
|
|
void *memcpy(void *to, const void *from, size_t n)
|
|
{
|
|
size_t i;
|
|
unsigned char *cto = to;
|
|
const unsigned char *cfrom = from;
|
|
|
|
for (i = 0; i < n; ++i, ++cto, ++cfrom)
|
|
*cto = *cfrom;
|
|
|
|
return to;
|
|
}
|
|
#endif
|
|
|
|
#ifndef ARCH_HAS_MEMCMP
|
|
int memcmp(const void *cs, const void *ct, size_t count)
|
|
{
|
|
const unsigned char *su1, *su2;
|
|
int res = 0;
|
|
|
|
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
|
|
if ((res = *su1 - *su2) != 0)
|
|
break;
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
#ifndef ARCH_HAS_MEMSET
|
|
void *memset(void *s, const int c, size_t count)
|
|
{
|
|
volatile char *dest = s;
|
|
size_t i = 0;
|
|
|
|
while (i < count)
|
|
dest[i++] = (char)c;
|
|
|
|
return s;
|
|
}
|
|
#endif
|
|
|
|
int std_strcmp(const char *cs, const char *ct)
|
|
{
|
|
unsigned char c1, c2;
|
|
|
|
while (1) {
|
|
c1 = *cs++;
|
|
c2 = *ct++;
|
|
if (c1 != c2)
|
|
return c1 < c2 ? -1 : 1;
|
|
if (!c1)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int std_strncmp(const char *cs, const char *ct, size_t count)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (cs[i] != ct[i])
|
|
return cs[i] < ct[i] ? -1 : 1;
|
|
if (!cs[i])
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|