]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Alter the qname bpf filter to make it const. 16452/head
authorMiod Vallat <miod.vallat@powerdns.com>
Fri, 7 Nov 2025 16:56:25 +0000 (17:56 +0100)
committerMiod Vallat <miod.vallat@powerdns.com>
Fri, 7 Nov 2025 18:14:04 +0000 (19:14 +0100)
This program needs to use a runtime value (a file descriptor), so in its
current form it is a non-const HUGE initializer, and causes the compiler
to emit a lot of code to initialize the local variable with the program
data, eating a lot of memory while there.

By changing the program to use a value of zero for the file descriptor,
the initializer becomes const; we need however to search for that
instruction and pach it before using the program, and this is not the
code I am most proud of... but when life gives you lemons, etc.

Signed-off-by: Miod Vallat <miod.vallat@powerdns.com>
pdns/dnsdistdist/bpf-filter.cc
pdns/dnsdistdist/bpf-filter.qname.ebpf

index afe9ff60216a98236486e780e2f663bca9a3e8c5..0a7161c80c6a846b99e647d21f836293bb195cca 100644 (file)
@@ -398,10 +398,47 @@ BPFFilter::BPFFilter(std::unordered_map<std::string, MapConfiguration>& configs,
 #include "bpf-filter.main.ebpf"
     };
 
-    const struct bpf_insn qname_filter[] = {
+    // ABANDON EVERY HOPE - SCARY CODE STARTS HERE
+
+    // This EBF program is huge, and unfortunately not constant; this causes
+    // the compiler to emit a lot of code (and eat a lot of memory to do so).
+    // Therefore we include an incomplete but constant version of the program
+    // and patch it locally, relying upon the fact that there is only one place
+    // to change.
+    // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays)
+    struct bpf_insn qname_filter[] = {
 #include "bpf-filter.qname.ebpf"
     };
 
+    // The program above contains
+    //   BPF_LD_MAP_FD(BPF_REG_1,0)
+    // instead of
+    //   BPF_LD_MAP_FD(BPF_REG_1,maps->d_qnames.d_fd.getHandle())
+    // and this is the only use of BPF_LD_MAP_FD in the program.
+    // We will search for that instruction, relying upon the fact that,
+    // in that particular program, there is only one such instruction.
+    {
+      unsigned int pos{0};
+      unsigned int limit{sizeof(qname_filter) / sizeof(struct bpf_insn)};
+      for (; pos < limit; ++pos) {
+        if (qname_filter[pos].code == (BPF_LD | BPF_DW | BPF_IMM)) { // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+          // We have found our instruction.
+          break;
+        }
+      }
+      // BPF_LD_MAP_FP actually is a sequence of two bpf instructions,
+      // because it loads a 64-bit value. So it can't be the last
+      // instruction either...
+      if (pos >= limit - 1) {
+        throw std::runtime_error("Assumption in the layout of the eBPF filter program no longer stands");
+      }
+      auto data = static_cast<__u64>(maps->d_qnames.d_fd.getHandle());
+      qname_filter[pos].imm = static_cast<__s32>(data); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+      qname_filter[pos + 1].imm = static_cast<__s32>(data >> 32); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+    }
+
+    // SCARY CODE ENDS HERE
+
     try {
       d_mainfilter = loadProgram(main_filter,
                                  sizeof(main_filter));
index 120e0620e2b7a14d23e635de60ebd4f382ac85b5..c31b0540f7e2b9049ca45f17282f133820229aaf 100644 (file)
@@ -4078,7 +4078,9 @@ BPF_MOV64_IMM(BPF_REG_9,255),
 BPF_ALU64_REG(BPF_ADD,BPF_REG_9,BPF_REG_7),
 BPF_RAW_INSN(BPF_LD|BPF_IND|BPF_H,BPF_REG_0,BPF_REG_9,0,0),
 BPF_MOV64_REG(BPF_REG_6,BPF_REG_0),
-BPF_LD_MAP_FD(BPF_REG_1,maps->d_qnames.d_fd.getHandle()),
+// This should be BPF_LD_MAP_FD(BPF_REG_1,maps->d_qnames.d_fd.getHandle())
+// but will be patched at runtime.
+BPF_LD_MAP_FD(BPF_REG_1,0),
 BPF_MOV64_REG(BPF_REG_2,BPF_REG_10),
 BPF_ALU64_IMM(BPF_ADD,BPF_REG_2,-256),
 BPF_RAW_INSN(BPF_JMP|BPF_CALL,0,0,0,BPF_FUNC_map_lookup_elem),