From 8b112ae4ee67020da0308fd01ac3278ecdcae100 Mon Sep 17 00:00:00 2001 From: Giuliano Belinassi Date: Thu, 17 Feb 2022 13:04:30 -0300 Subject: [PATCH] Add `livepatchable` command to check if a library is livepatchable The new command: $ ulp livepatchable can be used to check if a certain library is livepatchable. Signed-off-by: Giuliano Belinassi --- tests/Makefile.am | 1 + tests/livepatchable.py | 27 ++++++++++++++++ tests/testsuite.py | 14 ++++++++ tools/Makefile.am | 6 ++-- tools/arguments.h | 1 + tools/livepatchable.c | 72 ++++++++++++++++++++++++++++++++++++++++++ tools/livepatchable.h | 29 +++++++++++++++++ tools/post.c | 12 +++---- tools/post.h | 4 +++ tools/ulp.c | 26 ++++++++++++--- 10 files changed, 179 insertions(+), 13 deletions(-) create mode 100755 tests/livepatchable.py create mode 100644 tools/livepatchable.c create mode 100644 tools/livepatchable.h diff --git a/tests/Makefile.am b/tests/Makefile.am index 09ec2b18..cda87c32 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -490,6 +490,7 @@ TESTS = \ revert_and_patch.py \ process.py \ process_revert.py \ + livepatchable.py \ manyprocesses.py XFAIL_TESTS = \ diff --git a/tests/livepatchable.py b/tests/livepatchable.py new file mode 100755 index 00000000..a7153bd9 --- /dev/null +++ b/tests/livepatchable.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# libpulp - User-space Livepatching Library +# +# Copyright (C) 2021 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 . + +import testsuite + +if testsuite.is_library_livepatchable('.libs/libexception.so') == True: + exit(0) + +exit(1) diff --git a/tests/testsuite.py b/tests/testsuite.py index c16173ec..8b5e9edd 100755 --- a/tests/testsuite.py +++ b/tests/testsuite.py @@ -40,6 +40,20 @@ builddir = os.getcwd() ulptool = builddir + '/../tools/ulp' +# Check if certain library is livepatchable. +def is_library_livepatchable(library): + command = [ulptool, "livepatchable", library] + + try: + tool = subprocess.run(command, timeout=10, stderr=subprocess.STDOUT) + except subprocess.TimeoutExpired: + print('ulp tool deadlock'); + return False + + if tool.returncode == 0: + return True + return False + # Wrapper around pexpect.spawn that automatically sets userspace livepatching # requirements, such as LD_PRELOAD'ing libpulp.so, as well as extends its # functionality with live patching operations. diff --git a/tools/Makefile.am b/tools/Makefile.am index 57e23b03..5b08c63e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -31,7 +31,8 @@ noinst_HEADERS = \ dump.h \ post.h \ revert.h \ - messages.h + messages.h \ + livepatchable.h ulp_SOURCES = \ ulp.c \ @@ -45,7 +46,8 @@ ulp_SOURCES = \ revert.c \ messages.c \ introspection.c \ - ptrace.c + ptrace.c \ + livepatchable.c ulp_LDADD = $(top_builddir)/common/libcommon.la -lelf -ldl $(LIBUNWIND_LIBS) # Ensure access to the include directory diff --git a/tools/arguments.h b/tools/arguments.h index 55262184..68c30080 100644 --- a/tools/arguments.h +++ b/tools/arguments.h @@ -37,6 +37,7 @@ typedef enum ULP_POST, ULP_REVERSE, ULP_MESSAGES, + ULP_LIVEPATCHABLE, } command_t; struct arguments diff --git a/tools/livepatchable.c b/tools/livepatchable.c new file mode 100644 index 00000000..caaa755f --- /dev/null +++ b/tools/livepatchable.c @@ -0,0 +1,72 @@ +/* + * libpulp - User-space Livepatching Library + * + * Copyright (C) 2017-2021 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include "arguments.h" +#include "config.h" +#include "introspection.h" +#include "livepatchable.h" +#include "msg_queue.h" +#include "post.h" +#include "ulp_common.h" + +int +run_livepatchable(struct arguments *arguments) +{ + int ret = 0; + int fd; + + /* Set the verbosity level in the common introspection infrastructure. */ + ulp_verbose = arguments->verbose; + ulp_quiet = arguments->quiet; + + fd = open(arguments->args[0], 0); + if (fd == -1) + errx(EXIT_FAILURE, "Unable to open file '%s': %s.\n", arguments->args[0], + strerror(errno)); + + elf_version(EV_CURRENT); + Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + + struct Elf_Scn *scn = + find_section_by_name(elf, "__patchable_function_entries"); + if (scn == NULL) { + WARN("file '%s' is not livepatchable: Missing " + "__patchable_function_entries section.", + arguments->args[0]); + ret = 1; + } + else { + WARN("file '%s' is livepatchable.", arguments->args[0]); + ret = 0; + } + + elf_end(elf); + close(fd); + + return ret; +} diff --git a/tools/livepatchable.h b/tools/livepatchable.h new file mode 100644 index 00000000..714b0481 --- /dev/null +++ b/tools/livepatchable.h @@ -0,0 +1,29 @@ +/* + * libpulp - User-space Livepatching Library + * + * Copyright (C) 2017-2022 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 . + */ + +#ifndef LIVEPATCHABLE_H +#define LIVEPATCHABLE_H + +struct arguments; + +int run_livepatchable(struct arguments *); + +#endif /* LIVEPATCHABLE.H */ diff --git a/tools/post.c b/tools/post.c index b60b1e10..08f216bd 100644 --- a/tools/post.c +++ b/tools/post.c @@ -42,8 +42,8 @@ static Elf *elf; * such section is found. Exits in error if the string table containing * sections names is not found. */ -static Elf_Scn * -find_section_by_name(const char *name) +Elf_Scn * +find_section_by_name(Elf *elf, const char *name) { char *str; size_t string_table; @@ -95,9 +95,9 @@ merge_nops_at_addr(Elf64_Addr addr, size_t amount) } /* Use the .symtab if available, otherwise, the .dynsym. */ - scn = find_section_by_name(".symtab"); + scn = find_section_by_name(elf, ".symtab"); if (scn == NULL) - scn = find_section_by_name(".dynsym"); + scn = find_section_by_name(elf, ".dynsym"); if (scn == NULL) return; data = elf_getdata(scn, NULL); @@ -141,7 +141,7 @@ nops_fixup(void) Elf64_Shdr *shdr; Elf64_Addr addr; - scn = find_section_by_name("__patchable_function_entries"); + scn = find_section_by_name(elf, "__patchable_function_entries"); if (scn == NULL) return; data = elf_getdata(scn, NULL); @@ -183,7 +183,7 @@ run_post(struct arguments *arguments) assert(elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT)); /* Sanity check. */ - scn = find_section_by_name("__patchable_function_entries"); + scn = find_section_by_name(elf, "__patchable_function_entries"); if (scn == NULL) errx(EXIT_FAILURE, "Section __patchable_function_entries not found.\n" diff --git a/tools/post.h b/tools/post.h index 556b2c73..65b75365 100644 --- a/tools/post.h +++ b/tools/post.h @@ -23,6 +23,10 @@ #define POST_H struct arguments; +struct Elf_Scn; +struct Elf; + +struct Elf_Scn *find_section_by_name(struct Elf *, const char *name); int run_post(struct arguments *); diff --git a/tools/ulp.c b/tools/ulp.c index 2fbaaf17..3c9c565f 100644 --- a/tools/ulp.c +++ b/tools/ulp.c @@ -32,6 +32,7 @@ #include "config.h" #include "dump.h" #include "introspection.h" +#include "livepatchable.h" #include "messages.h" #include "packer.h" #include "patches.h" @@ -65,7 +66,8 @@ static const char doc[] = " with PID\n" " post Post process patch container (.so file) in ARG1.\n" " reverse Create reverse livepatch from metadata in ARG1.\n" -" messages Print livepatch information contained in libpulp.\n"; +" messages Print livepatch information contained in libpulp.\n" +" livepatchable Check if .so library in ARG1 is livepatchable.\n"; /* clang-format on */ @@ -117,10 +119,15 @@ command_from_string(const char *str) }; static const struct entry entries[] = { - { "patches", ULP_PATCHES }, { "check", ULP_CHECK }, - { "dump", ULP_DUMP }, { "packer", ULP_PACKER }, - { "trigger", ULP_TRIGGER }, { "post", ULP_POST }, - { "reverse", ULP_REVERSE }, { "messages", ULP_MESSAGES }, + { "patches", ULP_PATCHES }, + { "check", ULP_CHECK }, + { "dump", ULP_DUMP }, + { "packer", ULP_PACKER }, + { "trigger", ULP_TRIGGER }, + { "post", ULP_POST }, + { "reverse", ULP_REVERSE }, + { "messages", ULP_MESSAGES }, + { "livepatchable", ULP_LIVEPATCHABLE }, }; size_t i; @@ -206,6 +213,11 @@ handle_end_of_arguments(const struct argp_state *state) if (arguments->process_wildcard == 0) argp_error(state, "process is mandatory in 'messages' command."); break; + + case ULP_LIVEPATCHABLE: + if (state->arg_num < 2) { + argp_error(state, "file is mandatory in 'livepatchable' command."); + } } } @@ -324,6 +336,10 @@ main(int argc, char **argv) case ULP_MESSAGES: ret = run_messages(&arguments); break; + + case ULP_LIVEPATCHABLE: + ret = run_livepatchable(&arguments); + break; } return ret;