diff --git a/common/insn_queue.c b/common/insn_queue.c index adf25657..9401f9b0 100644 --- a/common/insn_queue.c +++ b/common/insn_queue.c @@ -98,8 +98,22 @@ insnq_interpret(insn_queue_t *queue) int num_insns = queue->num_insns; char *buffer = queue->buffer; + if ((unsigned) size > INSN_BUFFER_MAX) { + WARN("insnq: invalid instruction buffer size. All insns will be ignored."); + return EINSNQ; + } + while (num_insns_executed < num_insns) { struct ulp_insn *insn = (struct ulp_insn *)&buffer[pc]; + + /* Make sure we won't go past the buffer. */ + if (pc >= INSN_BUFFER_MAX) { + /* Abort if an invalid insn is received. */ + WARN("insnq: received instruction surpasses the buffer size. Further" + "insns will be ignored."); + return EINSNQ; + } + if (ulp_insn_valid(insn)) { pc += insn_interpret(insn); num_insns_executed++; diff --git a/include/insn_queue.h b/include/insn_queue.h index 2f4ae6b3..7bd05a0f 100644 --- a/include/insn_queue.h +++ b/include/insn_queue.h @@ -25,11 +25,11 @@ #include #include -/** Define a 2Mb buffer for holding the instructions list. */ -#define INSN_BUFFER_MAX (2 * 1024 * 1024) +/** Define a 256Kb buffer for holding the instructions list. */ +#define INSN_BUFFER_MAX (256 * 1024) /** Define the current version of the instruction queue. */ -#define INSNQ_CURR_VERSION 1 +#define INSNQ_CURR_VERSION 2 /** The ULP instruction queue. This works as follows: * 1- Libpulp write instructions that should be executed on the `ulp` tool diff --git a/include/msg_queue.h b/include/msg_queue.h index 83f17fe8..edbae203 100644 --- a/include/msg_queue.h +++ b/include/msg_queue.h @@ -24,8 +24,11 @@ #include -/** Define a 2Mb buffer for holding the messages. */ -#define MSGQ_BUFFER_MAX (2 * 1024 * 1024) +/** Define a 256Kb buffer for holding the messages in the old queue. */ +#define MSGQ_BUFFER_MAX (256 * 1024) + +/** Define a 2Mb buffer for holding the messages in the old queue. */ +#define MSGQ_OLD_BUFFER_MAX (2 * 1024 * 1024) /** This is the circular message queue datastructure. * @@ -60,10 +63,32 @@ * resulting in the circular queue behaviour. When reading this queue, the user * should start reading from the bottom position. */ + +/** Define the same structure that is used by old versions of libpulp (<0.3.18) */ struct msg_queue { + /** Size of the queue. Must match the size of MSGQ_BUFFER_MAX. */ + int size; + + /** Position pointing to free memory that can be written to. */ + int top; + + /** Position pointing to the oldest message still in buffer. */ + int bottom; + + /** Distance betweem top and bottom. Should not be greater than + * MSGQ_BUFFER_MAX. */ + int distance; + /** Buffer holding the messages. */ char buffer[MSGQ_BUFFER_MAX]; +}; + +/** Define the same structure that is used by old versions of libpulp (<0.3.18) */ +struct msg_queue_old +{ + /** Buffer holding the messages. */ + char buffer[MSGQ_OLD_BUFFER_MAX]; /** Position pointing to free memory that can be written to. */ int top; @@ -76,7 +101,7 @@ struct msg_queue int distance; }; -extern struct msg_queue __ulp_msg_queue; +extern struct msg_queue __ulp_msg_queue_new; void msgq_push(const char *format, ...); void msgq_vpush(const char *format, va_list); diff --git a/include/ulp_common.h b/include/ulp_common.h index 35687158..3e592e9c 100644 --- a/include/ulp_common.h +++ b/include/ulp_common.h @@ -33,9 +33,9 @@ #define OUT_PATCH_NAME "metadata.ulp" #define OUT_REVERSE_NAME "reverse.ulp" -/* Use a 512kb buffer for metadata. This should be enough for most case +/* Use a 64kb buffer for metadata. This should be enough for most case * scenarios. */ -#define ULP_METADATA_BUF_LEN (512 * 1024) +#define ULP_METADATA_BUF_LEN (64 * 1024) #define ULP_PATH_LEN 256 #define ARRAY_LENGTH(v) (sizeof(v) / sizeof(*(v))) diff --git a/lib/libpulp.versions b/lib/libpulp.versions index f583af6c..1202ee0b 100644 --- a/lib/libpulp.versions +++ b/lib/libpulp.versions @@ -23,6 +23,7 @@ __ulp_state; __ulp_get_global_universe; __ulp_msg_queue; + __ulp_msg_queue_new; __ulp_metadata_buffer; __ulp_error_state; __ulp_enable_or_disable_patching; diff --git a/lib/msg_queue.c b/lib/msg_queue.c index c220f1ac..1ef3de0d 100644 --- a/lib/msg_queue.c +++ b/lib/msg_queue.c @@ -30,18 +30,20 @@ /* Create an externally visible msg_queue object that will be read with ptrace. * It will be read by ulp_messages (tools/messages.c) using ptrace. */ -struct msg_queue __ulp_msg_queue; +struct msg_queue __ulp_msg_queue_new = { .size = MSGQ_BUFFER_MAX }; -static char msg[MSGQ_BUFFER_MAX]; +/* Define an buffer in which snprintf will expand tokens in strings. */ +#define SPRINTF_BUFFER_SIZE 8192 +static char msg[SPRINTF_BUFFER_SIZE]; static void msgq_strpush(const char *msg, size_t msg_size) { /* Write the msg_queue values in variables for briefness. */ - int top = __ulp_msg_queue.top; - int bottom = __ulp_msg_queue.bottom; - int distance = __ulp_msg_queue.distance; - char *buffer = __ulp_msg_queue.buffer; + int top = __ulp_msg_queue_new.top; + int bottom = __ulp_msg_queue_new.bottom; + int distance = __ulp_msg_queue_new.distance; + char *buffer = __ulp_msg_queue_new.buffer; /* In case the message is empty or it is too large for the buffer, don't * bother even trying to insert it. */ @@ -88,9 +90,9 @@ msgq_strpush(const char *msg, size_t msg_size) distance += msg_size; top = (top + msg_size) % MSGQ_BUFFER_MAX; - __ulp_msg_queue.top = top; - __ulp_msg_queue.bottom = bottom; - __ulp_msg_queue.distance = distance; + __ulp_msg_queue_new.top = top; + __ulp_msg_queue_new.bottom = bottom; + __ulp_msg_queue_new.distance = distance; } /* Push a message into the message queue. @@ -107,7 +109,7 @@ msgq_push(const char *format, ...) * return the size of the string, therefore, the size of the object will * be +1 because of the null character in the end of the string. */ va_start(arglist, format); - msg_size = vsnprintf(msg, MSGQ_BUFFER_MAX, format, arglist) + 1; + msg_size = vsnprintf(msg, SPRINTF_BUFFER_SIZE, format, arglist) + 1; va_end(arglist); msgq_strpush(msg, msg_size); @@ -125,7 +127,7 @@ msgq_vpush(const char *format, va_list arglist) /* Expand the format string with the arguments provided. vsnprintf will * return the size of the string, therefore, the size of the object will * be +1 because of the null character in the end of the string. */ - msg_size = vsnprintf(msg, MSGQ_BUFFER_MAX, format, arglist) + 1; + msg_size = vsnprintf(msg, SPRINTF_BUFFER_SIZE, format, arglist) + 1; msgq_strpush(msg, msg_size); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 6d0bfd66..7d4c9f98 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -714,7 +714,8 @@ TESTS = \ seccomp_disable.py \ run_libc.py \ glibc_private.py \ - dlopen.py + dlopen.py \ + size.sh XFAIL_TESTS = \ blocked.py \ diff --git a/tests/insn_queue.c b/tests/insn_queue.c index a2367bda..0cba7d68 100644 --- a/tests/insn_queue.c +++ b/tests/insn_queue.c @@ -91,6 +91,7 @@ test1_parent(int child_pid) close(1); if (insnq_interpret_from_process_(child_pid, (Elf64_Addr)&__ulp_insn_queue)) { + printf("Test 1 fail\n"); abort(); } fflush(stdout); @@ -104,7 +105,10 @@ test1_child(void) int n = INSN_BUFFER_MAX / 8; const char *string = "abc"; for (int i = 0; i < n; i++) { - insnq_insert_print(string); + if (insnq_insert_print(string)) { + printf("Error inserting print insn"); + abort (); + } } send('a'); @@ -120,6 +124,7 @@ test2_parent(int child_pid) wait_for('c'); if (insnq_interpret_from_process_( child_pid, (Elf64_Addr)&__ulp_insn_queue) != EOLDULP) { + printf("Test 2 fail\n"); abort(); } send('d'); @@ -180,6 +185,7 @@ test3_child(void) wait_for('f'); if (memcmp(buf, (void *)write_frame, 8) != 0) { + printf("Test 3 fail\n"); abort(); } } @@ -204,6 +210,7 @@ test4_child(void) /* Should detect that we are out of memory in the queue and fail. */ if (ret != EINSNQ) { + printf("Test 4 fail\n"); abort(); } diff --git a/tests/size.sh b/tests/size.sh new file mode 100755 index 00000000..2a73b655 --- /dev/null +++ b/tests/size.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# +# libpulp - User-space Livepatching Library +# +# Copyright (C) 2021-2025 SUSE Software Solutions GmbH +# +# This file is part of libpulp. +# +# libpulp is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# libpulp is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libpulp. If not, see . + +# Check if the code / variable size from libpulp does not surpass 1Mb + +SIZE=$(nm --print-size --size-sort --radix=d ../lib/.libs/libpulp.so.0 | awk '{ sum += $2 } END {print sum}') +echo "Size: $SIZE" + +if [ "x$SIZE" == "x" ]; then + exit 1 +fi + +if [ $SIZE -gt $(expr 1024 \* 1024) ]; then + exit 1 +fi + +exit 0 diff --git a/tools/introspection.c b/tools/introspection.c index 5cb5278d..da7abe8f 100644 --- a/tools/introspection.c +++ b/tools/introspection.c @@ -913,8 +913,12 @@ get_libpulp_extern_symbols(struct ulp_dynobj *obj, int pid) bitfield |= (1 << 3); } else if (!strcmp(remote_name, "_msg_queue")) { + obj->msg_queue_old = ehdr_addr + sym.st_value; + bitfield |= (1 << 4); // Use the same bitfield as the msg_queue. + } + else if (!strcmp(remote_name, "_msg_queue_new")) { obj->msg_queue = ehdr_addr + sym.st_value; - bitfield |= (1 << 4); + bitfield |= (1 << 4); // Use the same bitfield as the msg_queue. } else if (!strcmp(remote_name, "_revert_all")) { obj->revert_all = ehdr_addr + sym.st_value; diff --git a/tools/introspection.h b/tools/introspection.h index 60fe076c..507bd32f 100644 --- a/tools/introspection.h +++ b/tools/introspection.h @@ -110,6 +110,7 @@ struct ulp_dynobj Elf64_Addr check; Elf64_Addr state; Elf64_Addr global; + Elf64_Addr msg_queue_old; Elf64_Addr msg_queue; Elf64_Addr revert_all; Elf64_Addr metadata_buffer; diff --git a/tools/messages.c b/tools/messages.c index 62b3ba57..770cde2a 100644 --- a/tools/messages.c +++ b/tools/messages.c @@ -41,13 +41,21 @@ #include "ulp_common.h" static Elf64_Addr -get_msgq_address(const struct ulp_process *p) +get_msgq_address(const struct ulp_process *p, bool *old) { struct ulp_dynobj *dyn; Elf64_Addr msgq_addr = 0; for (dyn = p->dynobj_libpulp; dyn != NULL; dyn = dyn->next) { + + /* Try the old queue first. */ + if (dyn->msg_queue_old) { + *old = true; + msgq_addr = dyn->msg_queue_old; + } + if (dyn->msg_queue) { + *old = false; msgq_addr = dyn->msg_queue; break; } @@ -57,34 +65,31 @@ get_msgq_address(const struct ulp_process *p) } static void -msgq_print(struct msg_queue *msg_queue) +msgq_print(int size, int bottom, int distance, const char *buffer) { - int bottom = msg_queue->bottom; - int distance = msg_queue->distance; - while (distance > 0) { - putchar(msg_queue->buffer[bottom]); - bottom = (bottom + 1) % MSGQ_BUFFER_MAX; + putchar(buffer[bottom]); + bottom = (bottom + 1) % size; distance--; } } static void -msgq_debug(struct msg_queue *msg_queue) +msgq_debug(int size, int bottom, int top, const char *buffer) { int i; - for (i = 0; i < MSGQ_BUFFER_MAX; i++) { - if (msg_queue->buffer[i] == '\0') + for (i = 0; i < size; i++) { + if (buffer[i] == '\0') putchar('.'); else - putchar(msg_queue->buffer[i]); + putchar(buffer[i]); } putchar('\n'); - for (i = 0; i < MSGQ_BUFFER_MAX; i++) { - if (msg_queue->bottom == i) + for (i = 0; i < size; i++) { + if (bottom == i) putchar('B'); - else if (msg_queue->top == i) + else if (top == i) putchar('T'); else putchar(' '); @@ -93,46 +98,118 @@ msgq_debug(struct msg_queue *msg_queue) } static int -print_message_buffer(const struct ulp_process *p, bool debug) +print_message_buffer_new(int pid, Elf64_Addr msgq_addr, bool debug) { static struct msg_queue msg_queue; int ret; - Elf64_Addr msgq_addr = get_msgq_address(p); + memset(&msg_queue, 0, sizeof(msg_queue)); - memset(&msg_queue, 0, sizeof(struct msg_queue)); + if (attach(pid)) { + DEBUG("unable to attach to %d to read string.", pid); + return 1; + } - if (!msgq_addr) { - WARN("could not find libpulp.so message queue in process %d.", p->pid); + /* Read the first bytes without the buffer to determine the size. */ + ret = read_memory(&msg_queue, offsetof(struct msg_queue, buffer), pid, + msgq_addr); + + if (ret > 0) { + WARN("could not read libpulp.so message queue in process %d.", pid); return 1; } - if (attach(p->pid)) { - DEBUG("unable to attach to %d to read string.", p->pid); + if (msg_queue.size > MSGQ_BUFFER_MAX) { + WARN("libpulp.so message queue size is not valid.", pid); + msg_queue.size = MSGQ_BUFFER_MAX; + } + + /* Read the buffer now. */ + ret = read_memory(msg_queue.buffer, msg_queue.size, pid, + msgq_addr + offsetof(struct msg_queue, buffer)); + + if (detach(pid)) { + DEBUG("unable to detach from %d.", pid); return 1; } - ret = read_memory((void *)&msg_queue, sizeof(struct msg_queue), p->pid, - msgq_addr); + if (ret > 0) { + WARN("could not read libpulp.so message queue in process %d.", pid); + return 1; + } - if (detach(p->pid)) { - DEBUG("unable to detach from %d.", p->pid); + int size = msg_queue.size; + int bottom = msg_queue.bottom; + int top = msg_queue.top; + int distance = msg_queue.distance; + const char *buffer = msg_queue.buffer; + + if (debug) + msgq_debug(size, bottom, top, buffer); + else + msgq_print(size, bottom, distance, buffer); + + return ret; +} + +static int +print_message_buffer_old(int pid, Elf64_Addr msgq_addr, bool debug) +{ + static struct msg_queue_old msg_queue; + int ret; + + memset(&msg_queue, 0, sizeof(msg_queue)); + + if (attach(pid)) { + DEBUG("unable to attach to %d to read string.", pid); + return 1; + } + + ret = read_memory((void *)&msg_queue, sizeof(msg_queue), pid, msgq_addr); + + if (detach(pid)) { + DEBUG("unable to detach from %d.", pid); return 1; } if (ret > 0) { - WARN("could not read libpulp.so message queue in process %d.", p->pid); + WARN("could not read libpulp.so message queue in process %d.", pid); return 1; } + int size = MSGQ_OLD_BUFFER_MAX; + int bottom = msg_queue.bottom; + int top = msg_queue.top; + int distance = msg_queue.distance; + const char *buffer = msg_queue.buffer; + if (debug) - msgq_debug(&msg_queue); + msgq_debug(size, bottom, top, buffer); else - msgq_print(&msg_queue); + msgq_print(size, bottom, distance, buffer); return ret; } +static int +print_message_buffer(const struct ulp_process *p, bool debug) +{ + bool old; + + Elf64_Addr msgq_addr = get_msgq_address(p, &old); + + if (!msgq_addr) { + WARN("could not find libpulp.so message queue in process %d.", p->pid); + return 1; + } + + if (old) { + return print_message_buffer_old(p->pid, msgq_addr, debug); + } else { + return print_message_buffer_new(p->pid, msgq_addr, debug); + } +} + int run_messages(struct arguments *arguments) {