Skip to content

Commit

Permalink
Implement a insnq interpreter on libpulp side
Browse files Browse the repository at this point in the history
This commit adds a insnq_interp in libpulp side as a way to trigger a
livepatch without the `ulp` tool.  This is useful for debugging libpulp
as for example to use it in valgrind, since we can't run it under
addrsan.

Signed-off-by: Giuliano Belinassi <[email protected]>
  • Loading branch information
giulianobelinassi committed Nov 11, 2024
1 parent a45a67a commit 3317bd5
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 101 deletions.
5 changes: 4 additions & 1 deletion common/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ noinst_LTLIBRARIES = libcommon.la
# Ensure access to the include directory
AM_CFLAGS += -I$(abs_top_srcdir)/include

libcommon_la_SOURCES = common.c
# Add -fno-strict-alias to the insn_queue code.
insn_queue.lo : CFLAGS += -fno-strict-aliasing

libcommon_la_SOURCES = common.c insn_queue.c
128 changes: 128 additions & 0 deletions common/insn_queue.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* libpulp - User-space Livepatching Library
*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/

#include "ulp_common.h"
#include "insn_queue.h"
#include "error_common.h"

#include <assert.h>
#include <stdio.h>

/** @brief Interpret a print instruction.
*
* @param insn Instruction to interpet. Must be a print instruction.
*
* @return Size of interpreted instruction.
*/
int
insn_interpret_print(struct ulp_insn *insn)
{
struct ulp_insn_print *p_insn = (struct ulp_insn_print *)insn;

printf("%s\n", p_insn->bytes);
return insn->size;
}

/** @brief Interpret NOP instruction.
*
* @param insn Instruction to interpet. Must be a NOP instruction.
*
* @return Size of interpreted instruction (always 1 byte).
*/
int
insn_interpret_nop(struct ulp_insn *insn)
{
return sizeof(*insn);
}

/* Writes are specific from libpulp and libpulp-tools:
* - On tools, use ptrace.
* - On libpulp, set text permission and use memcpy.
*/
int
insn_interpret_write(struct ulp_insn *insn);

/** Table of decoders. Index must match the `enum ulp_insn_table` object. */
static int (*decoders[ULP_NUM_INSNS])(struct ulp_insn *insn) = {
insn_interpret_nop,
insn_interpret_print,
insn_interpret_write,
};

/** @brief Interpret the given instruction.
*
* This function will interpret the given instruction.
*
* @param insn Instruction to interpret.
*
* @return Size of instruction interpreted.
*/
int
insn_interpret(struct ulp_insn *insn)
{
int index = (int)insn->type;
return (decoders[index])(insn);
}

/** @brief Interpret the instructions in queue.
*
* Interpret all instructions inserted into the queue object.
*
* @param queue
*/
int
insnq_interpret(insn_queue_t *queue)
{
int pc = 0; /* Like a CPU program counter. */
int num_insns_executed = 0;

int size = queue->size;
int num_insns = queue->num_insns;
char *buffer = queue->buffer;

while (num_insns_executed < num_insns) {
struct ulp_insn *insn = (struct ulp_insn *)&buffer[pc];
if (ulp_insn_valid(insn)) {
pc += insn_interpret(insn);
num_insns_executed++;
}
else {
/* Abort if an invalid insn is received. */
WARN("insnq: invalid insn with opcode %d. Further insns will be "
"ignored.", (int)insn->type);
return EINSNQ;
}
}

/* The pc should stop at the size of the queue. */
if (pc != size) {
WARN("insnq: there are bytes left in the instruction queue");
return EINSNQ;
}

/* Number of instructions should match what is in the queue. */
if (num_insns_executed != num_insns) {
WARN("insnq: not all instructions executed");
return EINSNQ;
}

return 0;
}
9 changes: 9 additions & 0 deletions include/insn_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,13 @@ ulp_insn_valid(struct ulp_insn *insn)
}
}


/** @brief Interpret the instructions in queue.
*
* Interpret all instructions inserted into the queue object.
*
* @param queue
*/
int insnq_interpret(insn_queue_t *queue);

#endif /* INSNQ_H */
3 changes: 3 additions & 0 deletions include/insn_queue_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ ulp_error_t insnq_insert_write(void *addr, int n, const void *bytes);

int insnq_ensure_emptiness(void);

/** Interpret the global instruction queue from process side. */
int insnq_interpret_from_lib(void);

#endif
1 change: 1 addition & 0 deletions include/ulp_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <sys/types.h>

#define OUT_PATCH_NAME "metadata.ulp"
#define OUT_REVERSE_NAME "reverse.ulp"
Expand Down
26 changes: 24 additions & 2 deletions lib/gdb_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "ulp.h"
#include "minielf.h"
#include "error_common.h"
#include "insn_queue_lib.h"

extern char __ulp_metadata_buffer[ULP_METADATA_BUF_LEN];

Expand Down Expand Up @@ -90,6 +91,8 @@ inject_lp_path(const char *path, long metadata_size)
int
gdb_ulp_apply(const char *path)
{
int ret;

/* Prepare the ULP metadata buffer. */
memset(__ulp_metadata_buffer, '\0', ULP_METADATA_BUF_LEN);

Expand All @@ -105,13 +108,23 @@ gdb_ulp_apply(const char *path)
}

/* Trigger the livepatch. */
return __ulp_apply_patch();
if ((ret = __ulp_apply_patch()) != 0) {
return ret;
}

/* Process instruction queue. */
if ((ret = insnq_interpret_from_lib()) != 0) {
return ret;
}

return 0;
}


int
gdb_ulp_revert(const char *path)
{
int ret;
/* Prepare the ULP metadata buffer. */
memset(__ulp_metadata_buffer, '\0', ULP_METADATA_BUF_LEN);

Expand All @@ -127,5 +140,14 @@ gdb_ulp_revert(const char *path)
}

/* Trigger the livepatch. */
return __ulp_apply_patch();
if ((ret = __ulp_apply_patch()) != 0) {
return ret;
}

/* Process instruction queue. */
if ((ret = insnq_interpret_from_lib()) != 0) {
return ret;
}

return 0;
}
Loading

0 comments on commit 3317bd5

Please sign in to comment.