Skip to content

Commit

Permalink
(#1293) Verify funchook_install permission
Browse files Browse the repository at this point in the history
- the funhook install can fail in scenario where
  systemd `MemoryDenyWriteExecute` setting is on
```
  ...Attempts to create memory mappings that are writable and executable
  at the same time, or
  to change existing memory mappings to become executable, or
  mapping shared memory segments as executable, are prohibited...
```
  Ref: https://www.freedesktop.org/software/systemd/man/systemd.exec.html

  Closes: #1293
  • Loading branch information
michalbiesek committed Jan 31, 2023
1 parent 8c38ec2 commit d5c5e9b
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 11 deletions.
22 changes: 22 additions & 0 deletions os/linux/os.c
Expand Up @@ -950,3 +950,25 @@ osFindFd(pid_t pid, const char *fname)
free(cwd);
return fd;
}

/*
* Change protection for specified memory region using
* specified combintation flags and extraflags.
* Returns TRUE in case of operation success, FALSE otherwise
* Note: in case of success the `osMemPermRestore` should be called later
* with flags argument
*/
bool
osMemPermAllow(void *addr, size_t len, int flags, int extraflags) {
return scope_mprotect(addr, len, flags | extraflags) == 0;
}

/*
* Restore permission for specified memory region.
* Returns TRUE in case of operation success, FALSE otherwise
* Note: This function should be called in case of success of `osMemPermAllow`
*/
bool
osMemPermRestore(void *addr, size_t len, int flags) {
return scope_mprotect(addr, len, flags) == 0;
}
2 changes: 2 additions & 0 deletions os/linux/os.h
Expand Up @@ -68,5 +68,7 @@ extern long long osGetProcCPU(void);
extern uint64_t osFindLibrary(const char *, pid_t, bool);
extern int osFindFd(pid_t, const char *);
extern void osCreateSM(proc_id_t *, unsigned long);
extern bool osMemPermAllow(void *, size_t, int, int);
extern bool osMemPermRestore(void *, size_t, int);

#endif //__OS_H__
10 changes: 10 additions & 0 deletions os/macOS/os.c
Expand Up @@ -193,3 +193,13 @@ long long
osGetProcCPU(void) {
return -1;
}

bool
osMemPermAllow(void *addr, size_t len, int flags, int extraflags) {
return FALSE;
}

bool
osMemPermRestore(void *addr, size_t len, int flags) {
return FALSE;
}
2 changes: 2 additions & 0 deletions os/macOS/os.h
Expand Up @@ -48,3 +48,5 @@ extern int osNeedsConnect(int);
extern const char *osGetUserName(unsigned);
extern const char *osGetGroupName(unsigned);
extern long long osGetProcCPU(void);
extern bool osMemPermAllow(void *, size_t, int, int);
extern bool osMemPermRestore(void *, size_t, int);
10 changes: 5 additions & 5 deletions src/scopeelf.c
Expand Up @@ -201,9 +201,9 @@ doGotcha(struct link_map *lm, got_list_t *hook, Elf64_Rela *rel, Elf64_Sym *sym,

if (prot != -1) {
if ((prot & PROT_WRITE) == 0) {
// mprotect if write perms are not set
if (scope_mprotect((void *)saddr, (size_t)16, PROT_WRITE | prot) == -1) {
scopeLog(CFG_LOG_DEBUG, "doGotcha: mprotect failed");
// allow for write permission it write permission are not set
if (osMemPermAllow((void *)saddr, 16, prot, PROT_WRITE) == FALSE) {
scopeLog(CFG_LOG_DEBUG, "doGotcha: osMemPermAllow add write protection flag failed");
return -1;
}
}
Expand Down Expand Up @@ -239,8 +239,8 @@ doGotcha(struct link_map *lm, got_list_t *hook, Elf64_Rela *rel, Elf64_Sym *sym,

if ((prot & PROT_WRITE) == 0) {
// if we didn't mod above leave prot settings as is
if (scope_mprotect((void *)saddr, (size_t)16, prot) == -1) {
scopeLog(CFG_LOG_DEBUG, "doGotcha: mprotect failed");
if (osMemPermRestore((void *)saddr, 16, prot) == FALSE) {
scopeLog(CFG_LOG_DEBUG, "doGotcha: osMemPermRestore remove write memory protection flags failed");
return -1;
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/wrap.c
Expand Up @@ -1524,6 +1524,21 @@ initHook(int attachedFlag, bool scopedFlag)
if (should_we_patch || g_fn.__write_libc || g_fn.__write_pthread ||
((g_ismusl == FALSE) && g_fn.sendmmsg) ||
((g_ismusl == TRUE) && (g_fn.sendto || g_fn.recvfrom))) {

/*
* Check if we have proper permission to install funchook
* We need to be able to modify permission using (PROT_WRITE + PROT_EXEC) flags
*/
size_t testSize = 16;
void *ptr = scope_malloc(testSize);
if (osMemPermAllow(ptr, testSize, PROT_READ | PROT_WRITE, PROT_EXEC) == FALSE) {
scope_free(ptr);
scopeLogError("Interpose functions are limited (DNS, Console I/O). Please verify the MemoryDenyWriteExecute setting for following service: %s", g_proc.procname);
return;
}
scope_free(ptr);


funchook = funchook_create();

if (logLevel(g_log) <= CFG_LOG_TRACE) {
Expand Down
14 changes: 8 additions & 6 deletions src/wrap_go.c
Expand Up @@ -818,9 +818,11 @@ patchClone()
size_t pageSize = scope_getpagesize();
void *addr = (void *)((ptrdiff_t) clone & ~(pageSize - 1));

// set write perms on the page
if (scope_mprotect(addr, pageSize, PROT_WRITE | PROT_READ | PROT_EXEC)) {
scopeLogError("ERROR: patchCLone: mprotect failed\n");
const int perm = PROT_READ | PROT_EXEC;

// Add write permission on the page
if (osMemPermAllow(addr, pageSize, perm, PROT_WRITE) == FALSE) {
scopeLogError("ERROR: patchClone: osMemPermAllow failed\n");
return;
}

Expand All @@ -832,9 +834,9 @@ patchClone()

scopeLog(CFG_LOG_DEBUG, "patchClone: CLONE PATCHED\n");

// restore perms to the page
if (scope_mprotect(addr, pageSize, PROT_READ | PROT_EXEC)) {
scopeLogError("ERROR: patchCLone: mprotect restore failed\n");
// restore original permission to the page
if (osMemPermRestore(addr, pageSize, perm) == FALSE) {
scopeLogError("ERROR: patchClone: osMemPermRestore failed\n");
return;
}
}
Expand Down

0 comments on commit d5c5e9b

Please sign in to comment.