Skip to content

Commit

Permalink
Linux: gather permitted capabilities via capget(2)
Browse files Browse the repository at this point in the history
#1211 has shown reading /proc/<pid>/status might have a significant
performance impact.  It was started to be read by default to gather the
permitted capabilities of the process.

Gather permitted capabilities via the syscall capget(2) instead.
cap_get_proc(3) is not used to avoid linking with -lcap.
  • Loading branch information
cgzones committed Apr 6, 2024
1 parent eb27a94 commit b4d5b5c
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
}
break;
case USER:
if (this->elevated_priv)
if (this->elevated_priv == TRI_ON)
attr = CRT_colors[PROCESS_PRIV];
else if (host->htopUserId != this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
Expand Down
9 changes: 8 additions & 1 deletion Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ in the source distribution for its full text.

#define DEFAULT_HIGHLIGHT_SECS 5

typedef enum Tristate_ {
TRI_INITIAL = 0,
TRI_OFF = -1,
TRI_ON = 1,
} Tristate;


/* Core process states (shared by platforms)
* NOTE: The enum has an ordering that is important!
* See processStateChar in process.c for ProcessSate -> letter mapping */
Expand Down Expand Up @@ -106,7 +113,7 @@ typedef struct Process_ {
* - from file capabilities
* - inherited from the ambient set
*/
bool elevated_priv;
Tristate elevated_priv;

/* Process runtime (in hundredth of a second) */
unsigned long long int time;
Expand Down
23 changes: 15 additions & 8 deletions linux/LinuxProcessTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ in the source distribution for its full text.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
#include <unistd.h>
#include <linux/capability.h> // raw syscall, no libcap // IWYU pragma: keep // IWYU pragma: no_include <sys/capability.h>
#include <sys/stat.h>

#include "Compat.h"
Expand Down Expand Up @@ -391,14 +393,6 @@ static bool LinuxProcessTable_readStatusFile(Process* process, openat_arg_t proc
if (pid_ns_count > 1)
process->isRunningInContainer = true;

} else if (String_startsWith(buffer, "CapPrm:")) {
char* ptr = buffer + strlen("CapPrm:");
while (*ptr == ' ' || *ptr == '\t')
ptr++;

uint64_t cap_permitted = fast_strtoull_hex(&ptr, 16);
process->elevated_priv = cap_permitted != 0 && process->st_uid != 0;

} else if (String_startsWith(buffer, "voluntary_ctxt_switches:")) {
unsigned long vctxt;
int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt);
Expand Down Expand Up @@ -1481,6 +1475,19 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar
}
}

/* Gather permitted capabilities (thread-specific data) for non-root process. */
if (proc->st_uid != 0 && proc->elevated_priv != TRI_OFF) {
struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, .pid = Process_getPid(proc) };
struct __user_cap_data_struct data;

long res = syscall(SYS_capget, &header, &data);
if (res == 0) {
proc->elevated_priv = (data.permitted != 0) ? TRI_ON : TRI_OFF;
} else {
proc->elevated_priv = TRI_OFF;
}
}

if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessTable_readCGroupFile(lp, procFd);

Expand Down

0 comments on commit b4d5b5c

Please sign in to comment.