mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
Let's define two helpers strdupa_safe() + strndupa_safe() which do the same as their non-safe counterparts, except that they abort if called with allocations larger than ALLOCA_MAX. This should ensure that all our alloca() based allocations are subject to this limit. afaics glibc offers three alloca() based APIs: alloca() itself, strndupa() + strdupa(). With this we have now replacements for all of them, that take the limit into account.
158 lines
3.9 KiB
C
158 lines
3.9 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "percent-util.h"
|
|
#include "string-util.h"
|
|
#include "parse-util.h"
|
|
|
|
static int parse_parts_value_whole(const char *p, const char *symbol) {
|
|
const char *pc, *n;
|
|
int r, v;
|
|
|
|
pc = endswith(p, symbol);
|
|
if (!pc)
|
|
return -EINVAL;
|
|
|
|
n = strndupa_safe(p, pc - p);
|
|
r = safe_atoi(n, &v);
|
|
if (r < 0)
|
|
return r;
|
|
if (v < 0)
|
|
return -ERANGE;
|
|
|
|
return v;
|
|
}
|
|
|
|
static int parse_parts_value_with_tenths_place(const char *p, const char *symbol) {
|
|
const char *pc, *dot, *n;
|
|
int r, q, v;
|
|
|
|
pc = endswith(p, symbol);
|
|
if (!pc)
|
|
return -EINVAL;
|
|
|
|
dot = memchr(p, '.', pc - p);
|
|
if (dot) {
|
|
if (dot + 2 != pc)
|
|
return -EINVAL;
|
|
if (dot[1] < '0' || dot[1] > '9')
|
|
return -EINVAL;
|
|
q = dot[1] - '0';
|
|
n = strndupa_safe(p, dot - p);
|
|
} else {
|
|
q = 0;
|
|
n = strndupa_safe(p, pc - p);
|
|
}
|
|
r = safe_atoi(n, &v);
|
|
if (r < 0)
|
|
return r;
|
|
if (v < 0)
|
|
return -ERANGE;
|
|
if (v > (INT_MAX - q) / 10)
|
|
return -ERANGE;
|
|
|
|
v = v * 10 + q;
|
|
return v;
|
|
}
|
|
|
|
static int parse_parts_value_with_hundredths_place(const char *p, const char *symbol) {
|
|
const char *pc, *dot, *n;
|
|
int r, q, v;
|
|
|
|
pc = endswith(p, symbol);
|
|
if (!pc)
|
|
return -EINVAL;
|
|
|
|
dot = memchr(p, '.', pc - p);
|
|
if (dot) {
|
|
if (dot + 3 == pc) {
|
|
/* Support two places after the dot */
|
|
|
|
if (dot[1] < '0' || dot[1] > '9' || dot[2] < '0' || dot[2] > '9')
|
|
return -EINVAL;
|
|
q = (dot[1] - '0') * 10 + (dot[2] - '0');
|
|
|
|
} else if (dot + 2 == pc) {
|
|
/* Support one place after the dot */
|
|
|
|
if (dot[1] < '0' || dot[1] > '9')
|
|
return -EINVAL;
|
|
q = (dot[1] - '0') * 10;
|
|
} else
|
|
/* We do not support zero or more than two places */
|
|
return -EINVAL;
|
|
|
|
n = strndupa_safe(p, dot - p);
|
|
} else {
|
|
q = 0;
|
|
n = strndupa_safe(p, pc - p);
|
|
}
|
|
r = safe_atoi(n, &v);
|
|
if (r < 0)
|
|
return r;
|
|
if (v < 0)
|
|
return -ERANGE;
|
|
if (v > (INT_MAX - q) / 100)
|
|
return -ERANGE;
|
|
|
|
v = v * 100 + q;
|
|
return v;
|
|
}
|
|
|
|
int parse_percent_unbounded(const char *p) {
|
|
return parse_parts_value_whole(p, "%");
|
|
}
|
|
|
|
int parse_percent(const char *p) {
|
|
int v;
|
|
|
|
v = parse_percent_unbounded(p);
|
|
if (v > 100)
|
|
return -ERANGE;
|
|
|
|
return v;
|
|
}
|
|
|
|
int parse_permille_unbounded(const char *p) {
|
|
const char *pm;
|
|
|
|
pm = endswith(p, "‰");
|
|
if (pm)
|
|
return parse_parts_value_whole(p, "‰");
|
|
|
|
return parse_parts_value_with_tenths_place(p, "%");
|
|
}
|
|
|
|
int parse_permille(const char *p) {
|
|
int v;
|
|
|
|
v = parse_permille_unbounded(p);
|
|
if (v > 1000)
|
|
return -ERANGE;
|
|
|
|
return v;
|
|
}
|
|
|
|
int parse_permyriad_unbounded(const char *p) {
|
|
const char *pm;
|
|
|
|
pm = endswith(p, "‱");
|
|
if (pm)
|
|
return parse_parts_value_whole(p, "‱");
|
|
|
|
pm = endswith(p, "‰");
|
|
if (pm)
|
|
return parse_parts_value_with_tenths_place(p, "‰");
|
|
|
|
return parse_parts_value_with_hundredths_place(p, "%");
|
|
}
|
|
|
|
int parse_permyriad(const char *p) {
|
|
int v;
|
|
|
|
v = parse_permyriad_unbounded(p);
|
|
if (v > 10000)
|
|
return -ERANGE;
|
|
|
|
return v;
|
|
}
|