diff mbox series

[3/3] libselinux: correctly hash specfiles larger than 4G

Message ID 20220413155633.62677-3-cgzones@googlemail.com (mailing list archive)
State Accepted
Commit e17619792fa1
Headers show
Series [1/3] libselinux: correct parameter type in selabel_open(3) | expand

Commit Message

Christian Göttsche April 13, 2022, 3:56 p.m. UTC
The internal Sha1Update() functions only handles buffers up to a size of
UINT32_MAX, due to its usage of the type uint32_t.  This causes issues
when processing more than UINT32_MAX bytes, e.g. with a specfile larger
than 4G.  0aa974a4 ("libselinux: limit has buffer size") tried to
address this issue, but failed since the overflow check

    if (digest->hashbuf_size + buf_len < digest->hashbuf_size) {

will be done in the widest common type, which is size_t, the type of
`buf_len`.

Revert the type of `hashbuf_size` to size_t and instead process the data
in blocks of supported size.

Reverts: 0aa974a4 ("libselinux: limit has buffer size")
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
UBSAN reports without block processing:

hashbuf_size of uint32_t

    label_support.c:158:23: runtime error: implicit conversion from type 'unsigned long' of value 4294968207 (64-bit, unsigned) to type 'uint32_t' (aka 'unsigned int') changed the value to 911 (32-bit, unsigned)
        #0 0x4ecdbd in digest_add_specfile /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_support.c:158:23
        #1 0x4e0d83 in selabel_subs_init /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_file.c:666:6
        #2 0x4d5c48 in init /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_file.c:738:12
        #3 0x4d5c48 in selabel_file_init /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_file.c:1304:9
        #4 0x4cfdde in selabel_open /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label.c:228:6
        #5 0x4ce76c in main /home/christian/Coding/workspaces/selinux_userland/libselinux/utils/selabel_digest.c:130:8
        #6 0x77b65848a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
        #7 0x77b65848a277 in __libc_start_main csu/../csu/libc-start.c:409:3
        #8 0x41f4c0 in _start (/home/christian/Coding/workspaces/selinux_userland/libselinux/utils/selabel_digest+0x41f4c0)

hashbuf_size of size_t

label_support.c:125:40: runtime error: implicit conversion from type 'size_t' (aka 'unsigned long') of value 4298262406 (64-bit, unsigned) to type 'uint32_t' (aka 'unsigned int') changed the value to 3295110 (32-bit, unsigned)
        #0 0x4ec468 in digest_gen_hash /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_support.c:125:40
        #1 0x4d6dd3 in init /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_file.c:793:2
        #2 0x4d6dd3 in selabel_file_init /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label_file.c:1304:9
        #3 0x4cfdde in selabel_open /home/christian/Coding/workspaces/selinux_userland/libselinux/src/label.c:228:6
        #4 0x4ce76c in main /home/christian/Coding/workspaces/selinux_userland/libselinux/utils/selabel_digest.c:130:8
        #5 0x749229c371c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
        #6 0x749229c37277 in __libc_start_main csu/../csu/libc-start.c:409:3
        #7 0x41f4c0 in _start (/home/christian/Coding/workspaces/selinux_userland/libselinux/utils/selabel_digest+0x41f4c0)
---
 libselinux/src/label_internal.h |  2 +-
 libselinux/src/label_support.c  | 14 +++++++++++++-
 2 files changed, 14 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h
index 82a762f7..782c6aa8 100644
--- a/libselinux/src/label_internal.h
+++ b/libselinux/src/label_internal.h
@@ -57,7 +57,7 @@  int selabel_service_init(struct selabel_handle *rec,
 struct selabel_digest {
 	unsigned char *digest;	/* SHA1 digest of specfiles */
 	unsigned char *hashbuf;	/* buffer to hold specfiles */
-	uint32_t hashbuf_size;	/* buffer size */
+	size_t hashbuf_size;	/* buffer size */
 	size_t specfile_cnt;	/* how many specfiles processed */
 	char **specfile_list;	/* and their names */
 };
diff --git a/libselinux/src/label_support.c b/libselinux/src/label_support.c
index 94ed6e42..54fd49a5 100644
--- a/libselinux/src/label_support.c
+++ b/libselinux/src/label_support.c
@@ -116,13 +116,25 @@  int  read_spec_entries(char *line_buf, const char **errbuf, int num_args, ...)
 void  digest_gen_hash(struct selabel_digest *digest)
 {
 	Sha1Context context;
+	size_t remaining_size;
+	const unsigned char *ptr;
 
 	/* If SELABEL_OPT_DIGEST not set then just return */
 	if (!digest)
 		return;
 
 	Sha1Initialise(&context);
-	Sha1Update(&context, digest->hashbuf, digest->hashbuf_size);
+
+	/* Process in blocks of UINT32_MAX bytes */
+	remaining_size = digest->hashbuf_size;
+	ptr = digest->hashbuf;
+	while (remaining_size > UINT32_MAX) {
+		Sha1Update(&context, ptr, UINT32_MAX);
+		remaining_size -= UINT32_MAX;
+		ptr += UINT32_MAX;
+	}
+	Sha1Update(&context, ptr, remaining_size);
+
 	Sha1Finalise(&context, (SHA1_HASH *)digest->digest);
 	free(digest->hashbuf);
 	digest->hashbuf = NULL;