summaryrefslogtreecommitdiff
path: root/net/rsync/files/patch-lib_sysxattrs.c
blob: 16a307a83ad6b3bce17146b12896970a0eeda77d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
--- a/lib/sysxattrs.c.orig	2022-01-16 01:21:01.000000000 +0000
+++ b/lib/sysxattrs.c	2025-07-28 12:05:43.501532000 +0000
@@ -126,22 +126,29 @@
 	unsigned char keylen;
 	ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
 
-	if (len <= 0 || (size_t)len > size)
+	if (len <= 0 || size == 0)
 		return len;
 
+	if ((size_t)len == size) {
+		/* extattr_list_link fills a buffer with a list of file attributes.
+		 * If the buffer size is too small the content is truncated and
+		 * extattr_list_link returns the number of written bytes and no
+		 * error is raised. If size == len assumes the buffer was too small
+		 * and behave like Linux requesting a larger buffer. */
+		errno = ERANGE;
+		return -1;
+	}
+
 	/* FreeBSD puts a single-byte length before each string, with no '\0'
 	 * terminator.  We need to change this into a series of null-terminted
 	 * strings.  Since the size is the same, we can simply transform the
 	 * output in place. */
-	for (off = 0; off < len; off += keylen + 1) {
-		keylen = ((unsigned char*)list)[off];
-		if (off + keylen >= len) {
-			/* Should be impossible, but kernel bugs happen! */
-			errno = EINVAL;
-			return -1;
-		}
-		memmove(list+off, list+off+1, keylen);
-		list[off+keylen] = '\0';
+	keylen = (unsigned char)list[0];
+	memmove(list, list+1, len-1);
+	list[len-1] = '\0';
+	for (off = keylen; off < (len - 1); off += (keylen + 1)) {
+		keylen = (unsigned char)list[off];
+		list[off] = '\0';
 	}
 
 	return len;