1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates.
*/
#include <bpf_asm_helpers.h>
.text
/*
* For two buffers, return:
* 1 if the two buffers match in every bit
* 0 if the two buffers mismatch in any bit
* To help the BPF verifier, minimize branching by:
* - using a len whose exact value is known to the BPF verifier
* - operating on 64 bits at a time (len is a multiple of 8)
* - replacing conditional branching with arithmetic operations
* such as ^ | & etc.
*
* uint64_t dt_index_match(char *tmp1, char *tmp2, uint64_t len)
* {
* r0 = 0; // accumulate bits indicating mismatches
* r6 = 0; // loop counter
* L1:
* if (r6 >= len) goto L2;
*
* r4 = *((uint64_t*)&tmp1[r6]);
* r5 = *((uint64_t*)&tmp2[r6]);
* r0 |= (r4 ^ r5);
*
* r6 += 8;
* goto L1;
*
* L2:
* // value of r0
* // perfect match any mismatches
* // == 0 != 0
* r0 |= (r0 >> 32); // == 0 != 0
* r0 &= -1; // == 0 > 0
* r0 -= 1; // < 0 >= 0
* r0 >>= 63; // == 1 == 0
*
* return r0;
* }
*/
.align 4
.global dt_index_match
.type dt_index_match, @function
dt_index_match:
mov %r0, 0
mov %r6, 0
.L1:
jge %r6, %r3, .L2
mov %r4, %r1
add %r4, %r6
ldxdw %r4, [%r4+0]
mov %r5, %r2
add %r5, %r6
ldxdw %r5, [%r5+0]
xor %r4, %r5
or %r0, %r4
add %r6, 8
ja .L1
.L2:
mov %r4, %r0
rsh %r4, 32
or %r0, %r4
and %r0, -1
sub %r0, 1
rsh %r0, 63
exit
.size dt_index_match, .-dt_index_match
/*
* int dt_index(const char *s, const char *t, int start, char *tmp1, char *tmp2)
* {
* uint64_t r0, tlen;
* uint64_t buflen;
*
* // determine actual start index
* if (start < 0) start = 0;
*
* // round buflen for dt_index_match()
* buflen = STRSZ rounded up to multiple of 8;
*
* // keep a copy of t in tmp2
* tlen = bpf_probe_read_str(tmp2, buflen, t);
*
* // determine maximum possible index value
* maxi = bpf_probe_read_str(tmp1, buflen, s);
* maxi -= tlen;
*
* // drop terminating NULL
* tlen--;
*
* // Fill end of tmp1 with contents from tmp2
* // to suppress spurious mismatches.
* bpf_probe_read(tmp1 + tlen, buflen - tlen, tmp2 + tlen);
*
* Lloop:
* // check loop
* if (start > maxi) return -1;
*
* // fill start of tmp1 with s, starting at the proposed index
* bpf_probe_read(tmp1, tlen, s + start);
*
* // keep looping if not a match
* r0 = dt_index_match(tmp1, tmp2, buflen);
* start++;
* if (r0 == 0) goto Lloop;
*
* start--;
* return start;
* }
*
* Some variables are kept in registers or spilled to the stack:
* r6 = start [%fp+-8] = s
* r7 = tmp1 [%fp+-16] = buflen
* r8 = tmp2 [%fp+-24] = maxi
* r9 = tlen
* but t is not needed once we have copied its contents to tmp2.
*/
.align 4
.global dt_index
.type dt_index, @function
dt_index:
jsge %r3, 0, 1
mov %r3, 0 /* if (start < 0) start = 0 */
lddw %r6, STRSZ
add %r6, 7
and %r6, -8
stxdw [%fp+-16], %r6 /* buflen = STRSZ rounded up to multiple of 8 */
stxdw [%fp+-8], %r1 /* stash copies of some variables */
mov %r6, %r3
mov %r7, %r4
mov %r8, %r5
mov %r3, %r2
ldxdw %r2, [%fp+-16]
mov %r1, %r8
call BPF_FUNC_probe_read_str /* tlen = bpf_probe_read_str(tmp2, buflen, t) */
jsle %r0, 0, .Lerror
mov %r9, %r0
mov %r1, %r7
ldxdw %r2, [%fp+-16]
ldxdw %r3, [%fp+-8]
call BPF_FUNC_probe_read_str /* maxi = bpf_probe_read_str(tmp1, buflen, s) */
sub %r0, %r9 /* maxi -= tlen */
jslt %r0, 0, .Lerror
stxdw [%fp+-24], %r0
sub %r9, 1 /* tlen-- */
mov %r1, %r7
add %r1, %r9
ldxdw %r2, [%fp+-16]
sub %r2, %r9
mov %r3, %r8
add %r3, %r9
call BPF_FUNC_probe_read /* bpf_probe_read(tmp1 + tlen, buflen - tlen, tmp2 + tlen) */
.Lloop:
/* help the BPF verifier */
ldxdw %r0, [%fp+-16]
jge %r6, %r0, .Lerror /* if (start >= buflen) goto Lerror */
ldxdw %r0, [%fp+-24]
jgt %r6, %r0, .Lerror /* if (start > maxi) goto Lerror */
mov %r1, %r7
mov %r2, %r9
ldxdw %r3, [%fp+-8]
add %r3, %r6
call BPF_FUNC_probe_read /* bpf_probe_read(tmp1, tlen, s + start) */
mov %r1, %r7
mov %r2, %r8
ldxdw %r3, [%fp+-16]
call dt_index_match /* r0 = dt_index_match(tmp1, tmp2, buflen) */
add %r6, 1 /* start++ */
jeq %r0, 0, .Lloop /* if (r0 == 0) goto Lloop */
/* done */
sub %r6, 1 /* start-- */
mov %r0, %r6 /* return start */
exit
.Lerror:
mov %r0, -1
exit
.size dt_index, .-dt_index
|