[tz] [PROPOSED 6/6] Fix another theoretical zic.c overflow
Paul Eggert
eggert at cs.ucla.edu
Sun Nov 20 05:48:00 UTC 2022
* zic.c (size_overflow, size_sum): New functions.
(size_product, align_to, growalloc, relname):
Prefer ptrdiff_t to size_t where either will do.
(random_dirent, relname): Check for unlikely integer overflow.
---
zic.c | 49 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/zic.c b/zic.c
index 53351d0..a69d4e0 100644
--- a/zic.c
+++ b/zic.c
@@ -466,22 +466,35 @@ memory_exhausted(const char *msg)
exit(EXIT_FAILURE);
}
+static _Noreturn void
+size_overflow(void)
+{
+ memory_exhausted(_("size overflow"));
+}
+
static ATTRIBUTE_PURE ptrdiff_t
-size_product(ptrdiff_t nitems, size_t itemsize)
+size_sum(size_t a, size_t b)
+{
+ ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX);
+ if (a <= sum_max && b <= sum_max - a)
+ return a + b;
+ size_overflow();
+}
+
+static ATTRIBUTE_PURE ptrdiff_t
+size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
{
ptrdiff_t nitems_max = min(PTRDIFF_MAX, SIZE_MAX) / itemsize;
if (nitems <= nitems_max)
return nitems * itemsize;
- memory_exhausted(_("size overflow"));
+ size_overflow();
}
-static ATTRIBUTE_PURE size_t
-align_to(size_t size, size_t alignment)
+static ATTRIBUTE_PURE ptrdiff_t
+align_to(ptrdiff_t size, ptrdiff_t alignment)
{
- size_t lo_bits = alignment - 1;
- if (size <= SIZE_MAX - lo_bits)
- return size + (-size & lo_bits);
- memory_exhausted(_("alignment overflow"));
+ ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits);
+ return sum & ~lo_bits;
}
#if !HAVE_STRDUP
@@ -532,7 +545,8 @@ grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize)
}
static void *
-growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
+growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
+ ptrdiff_t *nitems_alloc)
{
return (nitems < *nitems_alloc
? ptr
@@ -1284,7 +1298,7 @@ random_dirent(char const **name, char **namealloc)
uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
if (!dst) {
- dst = emalloc(dirlen + prefixlen + suffixlen + 1);
+ dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
memcpy(dst, src, dirlen);
memcpy(dst + dirlen, prefix, prefixlen);
dst[dirlen + prefixlen + suffixlen] = '\0';
@@ -1363,19 +1377,20 @@ rename_dest(char *tempname, char const *name)
static char *
relname(char const *target, char const *linkname)
{
- size_t i, taillen, dotdotetcsize;
- size_t dir_len = 0, dotdots = 0, linksize = SIZE_MAX;
+ size_t i, taillen, dir_len = 0, dotdots = 0;
+ ptrdiff_t dotdotetcsize, linksize = min(PTRDIFF_MAX, SIZE_MAX);
char const *f = target;
char *result = NULL;
if (*linkname == '/') {
/* Make F absolute too. */
size_t len = strlen(directory);
- bool needslash = len && directory[len - 1] != '/';
- linksize = len + needslash + strlen(target) + 1;
+ size_t lenslash = len + (len && directory[len - 1] != '/');
+ size_t targetsize = strlen(target) + 1;
+ linksize = size_sum(lenslash, targetsize);
f = result = emalloc(linksize);
- strcpy(result, directory);
+ memcpy(result, directory, len);
result[len] = '/';
- strcpy(result + len + needslash, target);
+ memcpy(result + lenslash, target, targetsize);
}
for (i = 0; f[i] && f[i] == linkname[i]; i++)
if (f[i] == '/')
@@ -1383,7 +1398,7 @@ relname(char const *target, char const *linkname)
for (; linkname[i]; i++)
dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
taillen = strlen(f + dir_len);
- dotdotetcsize = 3 * dotdots + taillen + 1;
+ dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
if (dotdotetcsize <= linksize) {
if (!result)
result = emalloc(dotdotetcsize);
--
2.38.1
More information about the tz
mailing list