keyctl: use keyctl_read_alloc() in dump_key_tree_aux()

dump_key_tree_aux() (part of 'keyctl show') was racy: it allocated a
buffer for the keyring contents, then read the keyring.  But it's
possible that keys are added to the keyring concurrently.  This is
problematic for two reasons.  First, when keyctl_read() is passed a
buffer that is too small, it is unspecified whether it is filled or not.
Second, even if the buffer is filled, some keys (not necessarily even
the newest ones) would be omitted from the listing.

Switch to keyctl_read_alloc() which handles the "buffer too small" case
correctly by retrying the read.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
diff --git a/keyctl.c b/keyctl.c
index 2c8fdff..49a5ce4 100644
--- a/keyctl.c
+++ b/keyctl.c
@@ -2231,29 +2231,17 @@
 	/* if it's a keyring then we're going to want to recursively
 	 * display it if we can */
 	if (strcmp(type, "keyring") == 0) {
-		/* find out how big the keyring is */
-		ret = keyctl_read(key, NULL, 0);
-		if (ret < 0)
-			error("keyctl_read");
-		if (ret == 0)
-			return 0;
-		ringlen = ret;
-
 		/* read its contents */
-		payload = malloc(ringlen);
-		if (!payload)
-			error("malloc");
-
-		ret = keyctl_read(key, payload, ringlen);
+		ret = keyctl_read_alloc(key, &payload);
 		if (ret < 0)
-			error("keyctl_read");
+			error("keyctl_read_alloc");
 
-		ringlen = ret < ringlen ? ret : ringlen;
+		ringlen = ret;
 		kcount = ringlen / sizeof(key_serial_t);
 
 		/* walk the keyring */
 		pk = payload;
-		do {
+		while (ringlen >= sizeof(key_serial_t)) {
 			key = *pk++;
 
 			/* recurse into next keyrings */
@@ -2281,7 +2269,8 @@
 							    hex_key_IDs);
 			}
 
-		} while (ringlen -= 4, ringlen >= sizeof(key_serial_t));
+			ringlen -= sizeof(key_serial_t);
+		}
 
 		free(payload);
 	}