#ifndef __TAINT_H
#define __TAINT_H
#if defined(__clang__)
#define TAINT_UNROLL _Pragma("clang loop unroll(disable)")
#else
#define TAINT_UNROLL
#endif
#ifndef __always_inline
#define __always_inline inline __attribute__((always_inline))
#endif
#if defined(__bpf__)
#define TE_COPY(dst, n, src) bpf_probe_read_kernel((dst), (n), (src))
#else
#include <string.h>
#define TE_COPY(dst, n, src) memcpy((dst), (src), (n))
#endif
#ifndef TAINT_NOINLINE
#define TAINT_NOINLINE __attribute__((noinline))
#endif
#define TAINT_PAT_LEN 64
#define TAINT_SUF_MAX 24
#define TAINT_TEXT_BUF (TAINT_PAT_LEN + TAINT_SUF_MAX)
#define TAINT_ARG_LEN 24
#define TAINT_ARGV_CAP 128
#define MAX_ARG_SLOTS 16
#define TAINT_ARG_SLOTS_BUF (MAX_ARG_SLOTS * TAINT_ARG_LEN)
#define TAINT_COMM_LEN 16
#define MAX_TAINT_LABELS 64
#define MAX_TAINT_SOURCES 128
#define MAX_TAINT_RULES 128
#define MAX_TAINT_XFORMS 64
#define MAX_TAINT_GATES 64
#define MAX_TAINT_INVALS 64
#define TAINT_LABEL_NONE 0ULL
enum taint_match {
TAINT_MATCH_EXACT = 0,
TAINT_MATCH_PREFIX = 1,
TAINT_MATCH_SUFFIX = 2,
TAINT_MATCH_ANY = 3,
};
enum taint_src_kind {
TSRC_EXEC = 0,
TSRC_FILE = 1,
TSRC_ENDPOINT = 2,
};
enum taint_op {
TOP_EXEC = 0,
TOP_OPEN = 1,
TOP_WRITE = 2,
TOP_CONNECT = 3,
};
enum taint_cond {
TCOND_NONE = 0,
TCOND_LINEAGE = 1,
TCOND_AFTER = 2,
TCOND_TARGET = 3,
};
struct taint_inval {
unsigned char op;
unsigned char match;
char pat[TAINT_PAT_LEN];
};
enum taint_effect {
TEFFECT_AUDIT = 0,
TEFFECT_BLOCK = 1,
TEFFECT_KILL = 2,
};
struct taint_source {
unsigned char kind;
unsigned char match;
char pat[TAINT_PAT_LEN];
unsigned long long label;
unsigned int ipv4;
unsigned int ipv4_mask;
};
struct taint_rule {
unsigned char op;
unsigned char match;
unsigned char cond_kind;
unsigned char cond_neg;
unsigned char cond_match;
unsigned char effect;
char target[TAINT_PAT_LEN];
char arg[TAINT_ARG_LEN];
char cond_pat[TAINT_PAT_LEN];
unsigned long long req;
unsigned long long forbid;
unsigned long long gate;
unsigned int rule_id;
unsigned int ipv4;
unsigned int ipv4_mask;
unsigned int cond_ipv4;
unsigned int cond_ipv4_mask;
unsigned int gate_idx;
unsigned long long since_mask;
};
struct taint_xform {
unsigned char match;
unsigned char add;
char gate[TAINT_PAT_LEN];
unsigned long long label;
};
struct taint_gate {
unsigned char match;
char pat[TAINT_PAT_LEN];
unsigned long long bit;
};
struct taint_config {
unsigned int n_sources;
unsigned int n_rules;
unsigned int n_xforms;
unsigned int n_gates;
unsigned int n_invals;
struct taint_source sources[MAX_TAINT_SOURCES];
struct taint_rule rules[MAX_TAINT_RULES];
struct taint_xform xforms[MAX_TAINT_XFORMS];
struct taint_gate gates[MAX_TAINT_GATES];
struct taint_inval invals[MAX_TAINT_INVALS];
};
static __always_inline long te_nzmask(unsigned char c)
{
return -(long)(((unsigned int)c + 0xFFu) >> 8);
}
static __always_inline long te_iszero64(unsigned long x)
{
return ((long)((x | (0UL - x)) >> 63)) - 1;
}
static TAINT_NOINLINE int taint_streq(const char *a, const char *b)
{
long diff = 0;
TAINT_UNROLL
for (int i = 0; i < TAINT_PAT_LEN; i++)
diff |= (unsigned char)(a[i] ^ b[i]);
return diff == 0;
}
static TAINT_NOINLINE int taint_prefix(const char *text, const char *pre)
{
long diff = 0, anynz = 0;
TAINT_UNROLL
for (int i = 0; i < TAINT_PAT_LEN; i++) {
long m = te_nzmask((unsigned char)pre[i]);
anynz |= m;
diff |= m & (unsigned char)(text[i] ^ pre[i]);
}
return anynz != 0 && diff == 0;
}
static TAINT_NOINLINE int taint_suffix(const char *text, const char *suf)
{
int tn = 0, sn = 0;
long tlive = 1, slive = 1;
TAINT_UNROLL
for (int i = 0; i < TAINT_PAT_LEN; i++) {
tlive &= te_nzmask((unsigned char)text[i]) & 1;
tn += (int)tlive;
slive &= te_nzmask((unsigned char)suf[i]) & 1;
sn += (int)slive;
}
if (sn == 0 || sn > tn || sn > TAINT_SUF_MAX)
return 0;
int off = tn - sn;
char tail[TAINT_SUF_MAX] = {};
if (off < 0)
off = 0;
if (off > TAINT_PAT_LEN - 1)
off = TAINT_PAT_LEN - 1;
TE_COPY(tail, TAINT_SUF_MAX, text + off);
long diff = 0;
TAINT_UNROLL
for (int j = 0; j < TAINT_SUF_MAX; j++) {
long jm = -(long)(j < sn);
diff |= jm & (unsigned char)(tail[j] ^ (unsigned char)suf[j]);
}
return diff == 0;
}
static __always_inline int taint_match(unsigned int kind, const char *text,
const char *pat)
{
switch (kind) {
case TAINT_MATCH_PREFIX: return taint_prefix(text, pat);
case TAINT_MATCH_SUFFIX: return taint_suffix(text, pat);
case TAINT_MATCH_ANY: return 1;
default: return taint_streq(text, pat);
}
}
static __always_inline int taint_mask_ok(unsigned long long labels,
unsigned long long req,
unsigned long long forbid)
{
return (labels & req) == req && (labels & forbid) == 0ULL;
}
static TAINT_NOINLINE int taint_arg_match(const char *slots, const char *tok)
{
long found = te_iszero64((unsigned char)tok[0]);
TAINT_UNROLL
for (int s = 0; s < MAX_ARG_SLOTS; s++) {
long diff = 0;
const char *slot = slots + s * TAINT_ARG_LEN;
TAINT_UNROLL
for (int j = 0; j < TAINT_ARG_LEN; j++)
diff |= (unsigned char)(slot[j] ^ tok[j]);
found |= te_iszero64((unsigned long)diff);
}
return found != 0;
}
static __always_inline void te_tokenize_args(const char *blob, int len,
char *slots)
{
int si = 0, pj = 0;
if (len > TAINT_ARGV_CAP)
len = TAINT_ARGV_CAP;
for (int i = 0; i < TAINT_ARGV_CAP; i++) {
if (i >= len)
break;
char c = blob[i];
if (c == '\0') {
si++;
pj = 0;
if (si >= MAX_ARG_SLOTS)
break;
continue;
}
if (si < MAX_ARG_SLOTS && pj < TAINT_ARG_LEN - 1) {
int idx = si * TAINT_ARG_LEN + pj;
if (idx >= 0 && idx < MAX_ARG_SLOTS * TAINT_ARG_LEN)
slots[idx] = c;
pj++;
}
}
}
#endif