set(SRC
core/log.cpp
core/core.cpp
+ core/file.cpp
+ core/common.cpp
+ core/environ.cpp
core/injector.cpp
core/sys_calls.cpp
core/internal_libc.cpp
--- /dev/null
+#include "common.h"
+#include "log.h"
+#include "sys_calls.h"
+
+namespace __swap {
+
+void die()
+{
+ LOGE("Process died");
+ sys_exit(-1);
+}
+
+} // namespace __swap
--- /dev/null
+#ifndef COMMON_H
+#define COMMON_H
+
+namespace __swap {
+
+template <typename T, typename R>
+class AutoRelease {
+public:
+ AutoRelease(T &t, R &do_release) :
+ t_(t),
+ do_release_(do_release)
+ {
+ }
+
+ ~AutoRelease()
+ {
+ do_release_(t_);
+ }
+
+private:
+ T &t_;
+ R &do_release_;
+};
+
+template <typename T, typename R>
+AutoRelease<T, R> make_auto_release(T t, R r)
+{
+ return AutoRelease<T, R>(t, r);
+}
+
+
+void die();
+
+} // namespace __swap
+
+#endif // COMMON_H
--- /dev/null
+#include "environ.h"
+#include "common.h"
+#include "internal_libc.h"
+#include "log.h"
+#include "file.h"
+
+
+namespace __swap {
+namespace environ {
+
+static const char *parse_dec_val_backward(const char *begin, const char *end, uptr &val)
+{
+ val = 0;
+ uptr multipler = 1;
+ for (const char *it = end; it != begin; --it) {
+ char ch = *(it - 1);
+ if (ch < '0' || ch > '9')
+ return it;
+
+ val += multipler * (ch - '0');
+ multipler *= 10;
+ }
+
+ return begin;
+}
+
+static const char *skip_symbol_backward(const char *it, const char ch)
+{
+ if (*(--it) != ch) {
+ LOGE("Incorrect symbol: 0x%x ch=0x%x", 0xff & *it, ch);
+ return nullptr;
+ }
+ return it;
+}
+
+static const char *parse_dec_array_backward(const char *begin, const char *end,
+ uptr array[], uptr array_size)
+{
+ const char *it = end;
+ for (uptr i = 0; i < array_size; ++i) {
+ const char *new_it = parse_dec_val_backward(begin, it, array[i]);
+ if (new_it == it) {
+ LOGE("Value not found");
+ return nullptr;
+ }
+ it = new_it;
+
+ if (it <= begin) {
+ LOGE("Incorrect buffer");
+ return nullptr;
+ }
+
+ // skip space
+ it = skip_symbol_backward(it, ' ');
+ if (it <= begin) {
+ LOGE("Incorrect buffer");
+ return nullptr;
+ }
+ }
+
+ return it;
+}
+
+// In "/proc/self/stat", the values of the variables are arranged in the following order:
+// "... env_start env_end error_code\n".
+// This function converts the required values (env_start, env_end) from
+// a string representation to a numeric representation.
+static bool parse_stat(const char *begin, const char *end, uptr &env_start, uptr &env_end)
+{
+ const char *it = end;
+ if (it <= begin) {
+ LOGE("Incorrect buffer");
+ return false;
+ }
+
+ // skip '\n'
+ it = skip_symbol_backward(it, '\n');
+ if (it <= begin) {
+ LOGE("Incorrect buffer");
+ return false;
+ }
+
+ const uptr array_size = 3;
+ uptr array[array_size];
+
+ it = parse_dec_array_backward(begin, it, array, array_size);
+ if (it == nullptr) {
+ LOGE("Cannot parse stat");
+ return false;
+ }
+
+ env_start = array[2];
+ env_end = array[1];
+ return true;
+}
+
+void get_context(uptr &start, uptr &end)
+{
+ // '/proc/self/stat' contains:
+ // 1 - int: max 11 bytes)
+ // 1 - (string): max 18 bytes (2 parenthesis + TASK_COMM_LEN)
+ // 1 - char: max 1 byte
+ // 49 - ull/ll: max 20 bytes
+ // 51 - max 1 byte (spaces between variables)
+ //
+ // 11 + 18 + 1 + 49 * 20 + 51 = 1061
+ // Allocate the buffer with the stock: 2 * 1024.
+ // This buffer size should be sufficient to read the entire file '/proc/self/stat'.
+ const uptr buf_size = 2 * 1024;
+ char *buf = (char *)internal::malloc(buf_size);
+ if (!buf)
+ die();
+ auto auto_free_buf = make_auto_release(buf, internal::free);
+
+ uptr read_size;
+ if (!fs::read_to_buffer("/proc/self/stat", buf, buf_size, read_size)) {
+ LOGE("Cannot read file: '/proc/self/stat'");
+ die();
+ }
+
+ if (!parse_stat(buf, buf + read_size, start, end)) {
+ LOGE("Cannot parse file: '/proc/self/stat'");
+ die();
+ }
+
+ if (start > end) {
+ LOGE("Incorrect environ info: env_start=%lx env_end=%lx", start, end);
+ die();
+ }
+}
+
+} // namespace environ
+} // namespace __swap
--- /dev/null
+#ifndef ENVIRON_H
+#define ENVIRON_H
+
+#include "internal_deps.h"
+
+namespace __swap {
+namespace environ {
+
+void get_context(uptr &start, uptr &end);
+
+} // namespace environ
+} // namespace __swap
+
+#endif // ENVIRON_H
--- /dev/null
+#include "file.h"
+#include "log.h"
+#include "common.h"
+#include "sys_calls.h"
+#include <fcntl.h> // for O_RDONLY
+
+
+namespace __swap {
+namespace fs {
+
+bool read_to_buffer(const char *path, char *buf, uptr buf_size, uptr &read_size)
+{
+ int err;
+ sptr fd = sys_open(path, O_RDONLY, 0400);
+ if (syscall_is_error(fd, &err)) {
+ LOGE("Cannot open file: %s, errno=%d", path, err);
+ return false;
+ }
+
+ auto auto_close_fd = make_auto_release(fd, [](sptr fd) {
+ int err;
+ sptr ret = sys_close(fd);
+ if (syscall_is_error(ret, &err)) {
+ LOGE("Cannot close file: fd=%ld errno=%d", fd, err);
+ die();
+ }
+ });
+
+ sptr count = sys_read(fd, buf, buf_size);
+ if (syscall_is_error(count, &err)) {
+ LOGE("Cannot read file: %s, errno=%d", path, err);
+ return false;
+ }
+
+ if ((uptr)count >= buf_size) {
+ LOGE("Cannot read all file, buffer is small, size");
+ return false;
+ }
+
+ read_size = count;
+ return true;
+}
+
+} // namespace fs
+} // namespace __swap
--- /dev/null
+#ifndef FILE_H
+#define FILE_H
+
+#include "internal_deps.h"
+
+namespace __swap {
+namespace fs {
+
+bool read_to_buffer(const char *path, char *buf, uptr buf_size, uptr &read_size);
+
+} // namespace fs
+} // namespace __swap
+
+#endif // FILE_H
return dest;
}
+void *memset(void *s, int c, uptr n) noexcept
+{
+ char volatile *t = (char *)s;
+ for (uptr i = 0; i < n; ++i, ++t) {
+ *t = c;
+ }
+ return s;
+}
+
int strcmp(const char *s1, const char *s2) noexcept
{
for (;; ++s1, ++s2) {
void *memcpy(void *dest, const void *src, uptr n) noexcept;
+void *memset(void *s, int c, uptr n) noexcept;
int strcmp(const char *s1, const char *s2) noexcept;
int mprotect(void *addr, uptr len, int prot) noexcept;
uptr malloc_usable_size(void *ptr) noexcept;
#include <core/internal_deps.h>
#include <core/internal_libc.h>
#include <core/internal_libdl.h>
-
+#include <core/environ.h>
+#include <core/common.h>
#include "lsan_adapter.h"
+static const char lsan_environ[] = "LSAN_OPTIONS" // Required options:
+ "=detect_leaks=1" // - enable memory leak detection
+ ":log_path=/run/swap/tmp/lsan.log" // - set log path
+ ":leak_check_at_exit=0" // - disable because we ourselves called
+ // __lsan_do_leak_check()
+ ":exitcode=0" // - disable program exit with an error
+ // if the LSan found an error
+ ":stack_trace_format=' #%n %p %L'" // - set output format for stacktrace
+ ;
+
+
#define IS_CLEAR_MEMORY 1
#define CALLER_PC (uptr)__builtin_return_address(0)
#define CURRENT_FRAME (uptr)__builtin_frame_address(0)
return false;
}
+ // get environ space
+ uptr env_start, env_end;
+ environ::get_context(env_start, env_end);
+ uptr env_size = env_end - env_start;
+ LOGI("Environ space: [%lx..%lx] size=%ld", env_start, env_end, env_size);
+
+ if (env_size < sizeof(lsan_environ)) {
+ LOGE("Original environ size is very small, env_size=%ld lsan_eniron=%ld",
+ env_size, sizeof(lsan_environ));
+ return false;
+ }
+
+ // save env
+ char *env_backup = (char *)internal::malloc(env_size);
+ internal::memcpy(env_backup, (void *)env_start, env_size);
+
+ // change env
+ internal::memcpy((void *)env_start, lsan_environ, sizeof(lsan_environ));
+ internal::memset((void *)env_start + sizeof(lsan_environ), 0, env_size - sizeof(lsan_environ));
+
void *handle = internal::dlopen(lsan_path, RTLD_NOW);
+
+ // restore env
+ internal::memcpy((void *)env_start, env_backup, env_size);
+ internal::free(env_backup);
+
if (!handle) {
LOGE("Cannot load LSan library, path=%s", lsan_path);
return false;