From d72eb77f6e7a272889ad0eace21da52ae483800e Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Thu, 27 Jan 2022 14:22:22 -0500 Subject: [PATCH] Fix: conversion from KB to bytes overflow on arm32 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Observed issue ============== On enable channel the memory available check fails on arm32 when available memory, in bytes, is larger than 2^32. Cause ===== `read_proc_meminfo_field` converts the read value (in KB) to bytes and stores it into a size_t variable. On the system running the reproducer the value of the `value_kb` variable is 4839692, yielding an overflow when multiplied with 1024 since `size_t` is 32 bit long. `size_t` can be larger in certain situation (i.e LARGEFILE) but this is irrelevant to the problem at hand. Solution ======== Convert all the checks to use uint64_t. Known drawbacks ========= None. References ========== The multiplication overflow check scheme is borrowed from `src/common/time.c` Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I067da25659ab4115e5494e48aab45a1c35f56652 --- src/common/utils.c | 18 ++++++++---- src/common/utils.h | 4 +-- src/lib/lttng-ctl/lttng-ctl.c | 52 +++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/common/utils.c b/src/common/utils.c index 5aedb0889..febd75c18 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -7,6 +7,7 @@ */ #include "common/macros.h" +#include #define _LGPL_SOURCE #include #include @@ -1062,7 +1063,7 @@ end: } static -int read_proc_meminfo_field(const char *field, size_t *value) +int read_proc_meminfo_field(const char *field, uint64_t *value) { int ret; FILE *proc_meminfo; @@ -1080,10 +1081,10 @@ int read_proc_meminfo_field(const char *field, size_t *value) * field. */ while (!feof(proc_meminfo)) { - unsigned long value_kb; + uint64_t value_kb; ret = fscanf(proc_meminfo, - "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %lu kB\n", + "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %" SCNu64 " kB\n", name, &value_kb); if (ret == EOF) { /* @@ -1100,7 +1101,12 @@ int read_proc_meminfo_field(const char *field, size_t *value) * This number is displayed in kilo-bytes. Return the * number of bytes. */ - *value = ((size_t) value_kb) * 1024; + if (value_kb > UINT64_MAX / 1024) { + ERR("Overflow on kb to bytes conversion"); + break; + } + + *value = value_kb * 1024; ret = 0; goto found; } @@ -1120,7 +1126,7 @@ fopen_error: * a best guess. */ LTTNG_HIDDEN -int utils_get_memory_available(size_t *value) +int utils_get_memory_available(uint64_t *value) { return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value); } @@ -1130,7 +1136,7 @@ int utils_get_memory_available(size_t *value) * the information in `/proc/meminfo`. */ LTTNG_HIDDEN -int utils_get_memory_total(size_t *value) +int utils_get_memory_total(uint64_t *value) { return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value); } diff --git a/src/common/utils.h b/src/common/utils.h index 6eb8bf371..e09c0a9ae 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -47,8 +47,8 @@ int utils_create_lock_file(const char *filepath); int utils_recursive_rmdir(const char *path); int utils_truncate_stream_file(int fd, off_t length); int utils_show_help(int section, const char *page_name, const char *help_msg); -int utils_get_memory_available(size_t *value); -int utils_get_memory_total(size_t *value); +int utils_get_memory_available(uint64_t *value); +int utils_get_memory_total(uint64_t *value); int utils_change_working_directory(const char *path); enum lttng_error_code utils_user_id_from_name( const char *user_name, uid_t *user_id); diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index d72250b1e..c20052bf6 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -292,12 +293,14 @@ end: return ret; } -static int check_enough_available_memory(size_t num_bytes_requested_per_cpu) +static enum lttng_error_code check_enough_available_memory( + uint64_t num_bytes_requested_per_cpu) { int ret; + enum lttng_error_code ret_code; long num_cpu; - size_t best_mem_info; - size_t num_bytes_requested_total; + uint64_t best_mem_info; + uint64_t num_bytes_requested_total; /* * Get the number of CPU currently online to compute the amount of @@ -305,10 +308,18 @@ static int check_enough_available_memory(size_t num_bytes_requested_per_cpu) */ num_cpu = sysconf(_SC_NPROCESSORS_ONLN); if (num_cpu == -1) { - goto error; + ret_code = LTTNG_ERR_FATAL; + goto end; + } + + if (num_bytes_requested_per_cpu > UINT64_MAX / (uint64_t) num_cpu) { + /* Overflow */ + ret_code = LTTNG_ERR_OVERFLOW; + goto end; } - num_bytes_requested_total = num_bytes_requested_per_cpu * num_cpu; + num_bytes_requested_total = + num_bytes_requested_per_cpu * (uint64_t) num_cpu; /* * Try to get the `MemAvail` field of `/proc/meminfo`. This is the most @@ -330,10 +341,18 @@ static int check_enough_available_memory(size_t num_bytes_requested_per_cpu) goto success; } -error: - return -1; + /* No valid source of information. */ + ret_code = LTTNG_ERR_NOMEM; + goto end; + success: - return best_mem_info >= num_bytes_requested_total; + if (best_mem_info >= num_bytes_requested_total) { + ret_code = LTTNG_OK; + } else { + ret_code = LTTNG_ERR_NOMEM; + } +end: + return ret_code; } /* @@ -1584,9 +1603,10 @@ void lttng_channel_destroy(struct lttng_channel *channel) int lttng_enable_channel(struct lttng_handle *handle, struct lttng_channel *in_chan) { + enum lttng_error_code ret_code; int ret; struct lttcomm_session_msg lsm; - size_t total_buffer_size_needed_per_cpu = 0; + uint64_t total_buffer_size_needed_per_cpu = 0; /* NULL arguments are forbidden. No default values. */ if (handle == NULL || in_chan == NULL) { @@ -1626,10 +1646,20 @@ int lttng_enable_channel(struct lttng_handle *handle, * Verify that the amount of memory required to create the requested * buffer is available on the system at the moment. */ + if (lsm.u.channel.chan.attr.num_subbuf > + UINT64_MAX / lsm.u.channel.chan.attr.subbuf_size) { + /* Overflow */ + ret = -LTTNG_ERR_OVERFLOW; + goto end; + } + total_buffer_size_needed_per_cpu = lsm.u.channel.chan.attr.num_subbuf * lsm.u.channel.chan.attr.subbuf_size; - if (!check_enough_available_memory(total_buffer_size_needed_per_cpu)) { - return -LTTNG_ERR_NOMEM; + ret_code = check_enough_available_memory( + total_buffer_size_needed_per_cpu); + if (ret_code != LTTNG_OK) { + ret = -ret_code; + goto end; } lsm.cmd_type = LTTNG_ENABLE_CHANNEL; -- 2.34.1