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 4dd0986
Show file tree
Hide file tree
Showing 7 changed files with 62 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) == 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: 4 additions & 6 deletions src/scopeelf.c
Expand Up @@ -201,9 +201,8 @@ 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");
if (osMemPermAllow((void *)saddr, 16, prot, PROT_WRITE) == FALSE) {
scopeLog(CFG_LOG_DEBUG, "doGotcha: osMemPermAllow write protection flag failed");
return -1;
}
}
Expand Down Expand Up @@ -239,8 +238,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 unset write memory protection flags failed");
return -1;
}
}
Expand Down Expand Up @@ -407,4 +406,3 @@ is_musl(char *buf)

return FALSE;
}

15 changes: 15 additions & 0 deletions src/wrap.c
Expand Up @@ -1526,6 +1526,21 @@ initHook(int attachedFlag, bool scopedFlag)
((g_ismusl == TRUE) && (g_fn.sendto || g_fn.recvfrom))) {
funchook = funchook_create();

/*
* Check if we have permission to install funchook
* Verify if we can change existing memory mappings
*/
const int perm = PROT_READ | PROT_WRITE;
if (osMemPermAllow(funchook, 16, perm, PROT_EXEC) == FALSE) {
funchook_destroy(funchook);
scopeLogError("Interpose functions are limited (DNS, Console I/O). Please verify the MemoryDenyWriteExecute setting for following service: %s", g_proc.procname);
return;
}
if (osMemPermRestore(funchook, 16, perm) == FALSE) {
DBG(NULL);
scopeLogError("ERROR: initHook osMemPermRestore fails");
}

if (logLevel(g_log) <= CFG_LOG_TRACE) {
// TODO: add some mechanism to get the config'd log file path
funchook_set_debug_file(DEFAULT_LOG_PATH);
Expand Down
12 changes: 7 additions & 5 deletions src/wrap_go.c
Expand Up @@ -819,8 +819,11 @@ patchClone()
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;

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

Expand All @@ -832,9 +835,8 @@ 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");
if (osMemPermRestore(addr, pageSize, perm) == FALSE) {
scopeLogError("ERROR: patchClone: osMemPermRestore failed\n");
return;
}
}
Expand Down

0 comments on commit 4dd0986

Please sign in to comment.