Итак, через 3 дня, точнее 3 x 8 часов = 24 часа, достойных охоты за кодом, я думаю, что наконец-то нашел эту зудящую проблему.
Проблема была в some_inlined_func()
все это время, это было скорее сложно, чем сложно. Я записываю здесь шаблон кода, объясняющий проблему, чтобы другие могли увидеть и, надеюсь, потратить меньше 24 часов на головную боль; Я прошел через ад ради этого, так что оставайся сосредоточенным.
__alwais_inline static
int some_inlined_func(struct xdp_md *ctx, /* other non important args */)
{
if (!ctx)
return AN_ERROR_CODE;
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth;
struct iphdr *ipv4_hdr = NULL;
struct ipv6hdr *ipv6_hdr = NULL;
struct udphdr *udph;
uint16_t ethertype;
eth = (struct ethhdr *)data;
if (eth + 1 > data_end)
return AN_ERROR_CODE;
ethertype = __constant_ntohs(eth->h_proto);
if (ethertype == ETH_P_IP)
{
ipv4_hdr = (void *)eth + ETH_HLEN;
if (ipv4_hdr + 1 > data_end)
return AN_ERROR_CODE;
// stuff non related to the issue ...
} else if (ethertype == ETH_P_IPV6)
{
ipv6_hdr = (void *)eth + ETH_HLEN;
if (ipv6_hdr + 1 > data_end)
return AN_ERROR_CODE;
// stuff non related to the issue ...
} else
return A_RET_CODE_1;
/* here's the problem, but ... */
udph = (ipv4_hdr) ? ((void *)ipv4_hdr + sizeof(*ipv4_hdr)) :
((void *)ipv6_hdr + sizeof(*ipv6_hdr));
if (udph + 1 > data_end)
return AN_ERROR_CODE;
/* it actually breaks HERE, when dereferencing 'udph' */
uint16_t dst_port = __constant_ntohs(udph->dest);
// blablabla other stuff here unrelated to the problem ...
return A_RET_CODE_2;
}
Итак, почему он ломается в этот момент? Я думаю, это потому, что верификатор предполагает, что ipv6_hdr
потенциально может быть NULL
, что совершенно НЕПРАВИЛЬНО, потому что, если выполнение когда-либо доходит до этой точки, это только потому, что было установлено либо ipv4_hdr
, либо ipv6_hdr
(т. е. выполнение умирает до этой точки, если это случай ни IPv4, ни IPv6). Так что, по-видимому, верификатор не может этого сделать. Однако есть одна загвоздка: допустимо, если действительность также ipv6_hdr
проверяется явно, например так:
if (ipv4_hdr)
udph = (void *)ipv4_hdr + sizeof(*ipv4_hdr);
else if (ipv6_hdr)
udph = (void *)ipv6_hdr + sizeof(*ipv6_hdr);
else return A_RET_CODE_1; // this is redundant
Это также работает, если мы делаем это:
// "(ethertype == ETH_P_IP)" instead of "(ipv4_hdr)"
udph = (ethertype == ETH_P_IP) ? ((void *)ipv4_hdr + sizeof(*ipv4_hdr)) :
((void *)ipv6_hdr + sizeof(*ipv6_hdr));
Итак, мне кажется, что здесь есть что-то странное с верификатором, потому что он недостаточно умен (может быть, и не должен быть?), чтобы понять, что если он когда-либо дойдет до этого момента, то только потому, что ctx
относится либо к пакету IPv4, либо к пакету IPv6. .
Как все это объясняет жалобы на return act;
в entry_point()
? Просто, просто терпите меня. some_inlined_func()
не изменяет ctx
, а его оставшиеся аргументы не используются entry_point()
. Таким образом, в случае возврата act
, поскольку это зависит от результата some_inlined_func()
, выполняется some_inlined_func()
, и в этот момент верификатор жалуется. Но, в случае возврата XDP_<whatever>
, поскольку тело switch-case
, и ни some_inlined_func()
, не меняет внутреннего состояния программы/функции entry_point()
, компилятор (с O2) достаточно умен, чтобы понять, что нет смысла производить сборку для some_inlined_func()
и всего switch-case
(здесь это оптимизация O2). Таким образом, в заключение, в случае возврата XDP_<whatever>
, верификатор был счастлив, так как проблема на самом деле заключается в some_inlined_func()
, но фактическая сборка BPF не имеет ничего из этого, поэтому верификатор не проверял some_inlined_func()
, потому что не было никаких в первую очередь. Имеет смысл?
Известно ли такое "ограничение" БНФ? Есть ли вообще какой-либо документ, в котором указаны такие известные ограничения? Потому что я ничего не нашел.
person
pa5h1nh0
schedule
05.11.2018
r3
значение, хранящееся в*(r0 + 2)
, в то время, когдаr0
является скалярным значением.r0
обычно используется в качестве возвращаемого значения, используете ли вы какой-либо помощник BPF? Или, поскольку проблема связана сact
, вы так или иначе обновляетеact
в остальной части вашей программы? - person Qeole   schedule 04.11.2018act
обновляется только в этой веткеelse
... Я обновляю код, чтобы вы могли лучше понять, что происходит - person pa5h1nh0   schedule 04.11.2018clang
иllc
в версии 8.0.0, которая на данный момент еще не вышла, поэтому она нестабильна. Может ли это быть проблема? - person pa5h1nh0   schedule 04.11.2018