This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH v8] Implement strlcpy and strlcat [BZ #178]


On 2016-01-09 00:52, Florian Weimer wrote:
+size_t
+strlcat (char *__restrict dest, const char *__restrict src, size_t size)
+{
+  size_t src_length = strlen (src);
+
+  /* Our implementation strlcat supports dest == NULL if size == 0
+     (for consistency with snprintf and strlcpy), but strnlen does
+     not, so we have to cover this case explicitly.  */
+  if (size == 0)
+    return src_length;
+
+  size_t dest_length = __strnlen (dest, size);
+  if (dest_length != size)
+    {
+      /* Copy at most the remaining number of characters in the
+	 destination buffer.  Leave for the NUL terminator.  */
+      size_t to_copy = size - dest_length - 1;
+      /* But not more than what is available in the source string.  */
+      if (to_copy > src_length)
+	to_copy = src_length;
+
+      char *target = dest + dest_length;
+      memcpy (target, src, to_copy);
+      target[to_copy] = '\0';
+    }
+
+  /* The sum cannot wrap around because both strings would be larger
+     than half of the address space, which is not possible due to
+     the restrict qualifier.  */
+  _Static_assert (sizeof (uintptr_t) == sizeof (size_t),
+		  "theoretical maximum object size covers address space");
+  return dest_length + src_length;
+}

First, I don't think the last comment is fully accurate -- both strings are not required to be larger than half of the address space each, 1.5GB and 3GB are enough for wrapping with 32-bit size_t.

Second, AIUI the full story is like this: strlcat (as documented in the latest version) requires @var{to} to contain a null byte in its first @var{size} bytes, hence @var{to} and @var{from} both being a sequence of non-nulls followed by a mandatory null can overlap only when their trailing nulls are the same. But the trailing null in @var{to} is overwritten while the trailing null in @var{from} is read which contradicts the restrict qualifier. Hence the strings don't overlap.

If you permit @var{to} to be non-null-terminated the situation is worse -- it doesn't write anything and resrict doesn't come into play. Then the following is defined and should return 5GB on 32-bit arch:

  size_t gb = (size_t)1024 * 1024 * 1024;
  s = malloc(3 * gb);
  memset(s, 'A', 3 * gb);
  strlcat(s, s, 2 * gb);

--
Alexander Cherepanov


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]