| @@ -1421,6 +1421,215 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn, · 1420 unmodified lines |
| 1421 | } | 1421 | } |
| 1422 | } | 1422 | } |
| 1423 | | 1423 | |
| | 1424 | /* |
| | 1425 | * Record access_bytes from helper/kfunc or load/store insn. |
| | 1426 | * access_bytes > 0: stack read |
| | 1427 | * access_bytes < 0: stack write |
| | 1428 | * access_bytes == S64_MIN: unknown — conservative, mark [0..slot] as read |
| | 1429 | * access_bytes == 0: no access |
| | 1430 | * |
| | 1431 | */ |
| | 1432 | static int record_stack_access_off(struct bpf_verifier_env *env, |
| | 1433 | struct func_instance *instance, s64 fp_off, |
| | 1434 | s64 access_bytes, u32 frame, u32 insn_idx) |
| | 1435 | { |
| | 1436 | s32 slot_hi, slot_lo; |
| | 1437 | spis_t mask; |
| | 1438 | |
| | 1439 | if (fp_off >= 0) |
| | 1440 | /* |
| | 1441 | * out of bounds stack access doesn't contribute |
| | 1442 | * into actual stack liveness. It will be rejected |
| | 1443 | * by the main verifier pass later. |
| | 1444 | */ |
| | 1445 | return 0; |
| | 1446 | if (access_bytes == S64_MIN) { |
| | 1447 | /* helper/kfunc read unknown amount of bytes from fp_off until fp+0 */ |
| | 1448 | slot_hi = (-fp_off - 1) / STACK_SLOT_SZ; |
| | 1449 | mask = SPIS_ZERO; |
| | 1450 | spis_or_range(&mask, 0, slot_hi); |
| | 1451 | return mark_stack_read(instance, frame, insn_idx, mask); |
| | 1452 | } |
| | 1453 | if (access_bytes > 0) { |
| | 1454 | /* Mark any touched slot as use */ |
| | 1455 | slot_hi = (-fp_off - 1) / STACK_SLOT_SZ; |
| | 1456 | slot_lo = max_t(s32, (-fp_off - access_bytes) / STACK_SLOT_SZ, 0); |
| | 1457 | mask = SPIS_ZERO; |
| | 1458 | spis_or_range(&mask, slot_lo, slot_hi); |
| | 1459 | return mark_stack_read(instance, frame, insn_idx, mask); |
| | 1460 | } else if (access_bytes < 0) { |
| | 1461 | /* Mark only fully covered slots as def */ |
| | 1462 | access_bytes = -access_bytes; |
| | 1463 | slot_hi = (-fp_off) / STACK_SLOT_SZ - 1; |
| | 1464 | slot_lo = max_t(s32, (-fp_off - access_bytes + STACK_SLOT_SZ - 1) / STACK_SLOT_SZ, 0); |
| | 1465 | if (slot_lo <= slot_hi) { |
| | 1466 | mask = SPIS_ZERO; |
| | 1467 | spis_or_range(&mask, slot_lo, slot_hi); |
| | 1468 | bpf_mark_stack_write(env, frame, mask); |
| | 1469 | } |
| | 1470 | } |
| | 1471 | return 0; |
| | 1472 | } |
| | 1473 | |
| | 1474 | /* |
| | 1475 | * 'arg' is FP-derived argument to helper/kfunc or load/store that |
| | 1476 | * reads (positive) or writes (negative) 'access_bytes' into 'use' or 'def'. |
| | 1477 | */ |
| | 1478 | static int record_stack_access(struct bpf_verifier_env *env, |
| | 1479 | struct func_instance *instance, |
| | 1480 | const struct arg_track *arg, |
| | 1481 | s64 access_bytes, u32 frame, u32 insn_idx) |
| | 1482 | { |
| | 1483 | int i, err; |
| | 1484 | |
| | 1485 | if (access_bytes == 0) |
| | 1486 | return 0; |
| | 1487 | if (arg->off_cnt == 0) { |
| | 1488 | if (access_bytes > 0 || access_bytes == S64_MIN) |
| | 1489 | return mark_stack_read(instance, frame, insn_idx, SPIS_ALL); |
| | 1490 | return 0; |
| | 1491 | } |
| | 1492 | if (access_bytes != S64_MIN && access_bytes < 0 && arg->off_cnt != 1) |
| | 1493 | /* multi-offset write cannot set stack_def */ |
| | 1494 | return 0; |
| | 1495 | |
| | 1496 | for (i = 0; i < arg->off_cnt; i++) { |
| | 1497 | err = record_stack_access_off(env, instance, arg->off[i], access_bytes, frame, insn_idx); |
| | 1498 | if (err) |
| | 1499 | return err; |
| | 1500 | } |
| | 1501 | return 0; |
| | 1502 | } |
| | 1503 | |
| | 1504 | /* |
| | 1505 | * When a pointer is ARG_IMPRECISE, conservatively mark every frame in |
| | 1506 | * the bitmask as fully used. |
| | 1507 | */ |
| | 1508 | static int record_imprecise(struct func_instance *instance, u32 mask, u32 insn_idx) |
| | 1509 | { |
| | 1510 | int depth = instance->callchain.curframe; |
| | 1511 | int f, err; |
| | 1512 | |
| | 1513 | for (f = 0; mask; f++, mask >>= 1) { |
| | 1514 | if (!(mask & 1)) |
| | 1515 | continue; |
| | 1516 | if (f <= depth) { |
| | 1517 | err = mark_stack_read(instance, f, insn_idx, SPIS_ALL); |
| | 1518 | if (err) |
| | 1519 | return err; |
| | 1520 | } |
| | 1521 | } |
| | 1522 | return 0; |
| | 1523 | } |
| | 1524 | |
| | 1525 | /* Record load/store access for a given 'at' state of 'insn'. */ |
| | 1526 | static int record_load_store_access(struct bpf_verifier_env *env, |
| | 1527 | struct func_instance *instance, |
| | 1528 | struct arg_track *at, int insn_idx) |
| | 1529 | { |
| | 1530 | struct bpf_insn *insn = &env->prog->insnsi[insn_idx]; |
| | 1531 | int depth = instance->callchain.curframe; |
| | 1532 | s32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code)); |
| | 1533 | u8 class = BPF_CLASS(insn->code); |
| | 1534 | struct arg_track resolved, *ptr; |
| | 1535 | int oi; |
| | 1536 | |
| | 1537 | switch (class) { |
| | 1538 | case BPF_LDX: |
| | 1539 | ptr = &at[insn->src_reg]; |
| | 1540 | break; |
| | 1541 | case BPF_STX: |
| | 1542 | if (BPF_MODE(insn->code) == BPF_ATOMIC) { |
| | 1543 | if (insn->imm == BPF_STORE_REL) |
| | 1544 | sz = -sz; |
| | 1545 | if (insn->imm == BPF_LOAD_ACQ) |
| | 1546 | ptr = &at[insn->src_reg]; |
| | 1547 | else |
| | 1548 | ptr = &at[insn->dst_reg]; |
| | 1549 | } else { |
| | 1550 | ptr = &at[insn->dst_reg]; |
| | 1551 | sz = -sz; |
| | 1552 | } |
| | 1553 | break; |
| | 1554 | case BPF_ST: |
| | 1555 | ptr = &at[insn->dst_reg]; |
| | 1556 | sz = -sz; |
| | 1557 | break; |
| | 1558 | default: |
| | 1559 | return 0; |
| | 1560 | } |
| | 1561 | |
| | 1562 | /* Resolve offsets: fold insn->off into arg_track */ |
| | 1563 | if (ptr->off_cnt > 0) { |
| | 1564 | resolved.off_cnt = ptr->off_cnt; |
| | 1565 | resolved.frame = ptr->frame; |
| | 1566 | for (oi = 0; oi < ptr->off_cnt; oi++) { |
| | 1567 | resolved.off[oi] = arg_add(ptr->off[oi], insn->off); |
| | 1568 | if (resolved.off[oi] == OFF_IMPRECISE) { |
| | 1569 | resolved.off_cnt = 0; |
| | 1570 | break; |
| | 1571 | } |
| | 1572 | } |
| | 1573 | ptr = &resolved; |
| | 1574 | } |
| | 1575 | |
| | 1576 | if (ptr->frame >= 0 && ptr->frame <= depth) |
| | 1577 | return record_stack_access(env, instance, ptr, sz, ptr->frame, insn_idx); |
| | 1578 | if (ptr->frame == ARG_IMPRECISE) |
| | 1579 | return record_imprecise(instance, ptr->mask, insn_idx); |
| | 1580 | /* ARG_NONE: not derived from any frame pointer, skip */ |
| | 1581 | return 0; |
| | 1582 | } |
| | 1583 | |
| | 1584 | /* Record stack access for a given 'at' state of helper/kfunc 'insn' */ |
| | 1585 | static int record_call_access(struct bpf_verifier_env *env, |
| | 1586 | struct func_instance *instance, |
| | 1587 | struct arg_track *at, |
| | 1588 | int insn_idx) |
| | 1589 | { |
| | 1590 | struct bpf_insn *insn = &env->prog->insnsi[insn_idx]; |
| | 1591 | int depth = instance->callchain.curframe; |
| | 1592 | struct bpf_call_summary cs; |
| | 1593 | int r, err = 0, num_params = 5; |
| | 1594 | |
| | 1595 | if (bpf_pseudo_call(insn)) |
| | 1596 | return 0; |
| | 1597 | |
| | 1598 | if (bpf_get_call_summary(env, insn, &cs)) |
| | 1599 | num_params = cs.num_params; |
| | 1600 | |
| | 1601 | for (r = BPF_REG_1; r < BPF_REG_1 + num_params; r++) { |
| | 1602 | int frame = at[r].frame; |
| | 1603 | s64 bytes; |
| | 1604 | |
| | 1605 | if (!arg_is_fp(&at[r])) |
| | 1606 | continue; |
| | 1607 | |
| | 1608 | if (bpf_helper_call(insn)) { |
| | 1609 | bytes = bpf_helper_stack_access_bytes(env, insn, r - 1, insn_idx); |
| | 1610 | } else if (bpf_pseudo_kfunc_call(insn)) { |
| | 1611 | bytes = bpf_kfunc_stack_access_bytes(env, insn, r - 1, insn_idx); |
| | 1612 | } else { |
| | 1613 | for (int f = 0; f <= depth; f++) { |
| | 1614 | err = mark_stack_read(instance, f, insn_idx, SPIS_ALL); |
| | 1615 | if (err) |
| | 1616 | return err; |
| | 1617 | } |
| | 1618 | return 0; |
| | 1619 | } |
| | 1620 | if (bytes == 0) |
| | 1621 | continue; |
| | 1622 | |
| | 1623 | if (frame >= 0 && frame <= depth) |
| | 1624 | err = record_stack_access(env, instance, &at[r], bytes, frame, insn_idx); |
| | 1625 | else if (frame == ARG_IMPRECISE) |
| | 1626 | err = record_imprecise(instance, at[r].mask, insn_idx); |
| | 1627 | if (err) |
| | 1628 | return err; |
| | 1629 | } |
| | 1630 | return 0; |
| | 1631 | } |
| | 1632 | |
| 1424 | /* | 1633 | /* |
| 1425 | * For a calls_callback helper, find the callback subprog and determine | 1634 | * For a calls_callback helper, find the callback subprog and determine |
| 1426 | * which caller register maps to which callback register for FP passthrough. | 1635 | * which caller register maps to which callback register for FP passthrough. |
| @@ -1665,6 +1874,40 @@ redo: · 238 unmodified lines |
| 1665 | if (changed) | 1874 | if (changed) |
| 1666 | goto redo; | 1875 | goto redo; |
| 1667 | | 1876 | |
| | 1877 | /* Record memory accesses using converged at_in (RPO skips dead code) */ |
| | 1878 | for (p = po_end - 1; p >= po_start; p--) { |
| | 1879 | int idx = env->cfg.insn_postorder[p]; |
| | 1880 | int i = idx - start; |
| | 1881 | struct bpf_insn *insn = &insns[idx]; |
| | 1882 | |
| | 1883 | reset_stack_write_marks(env, instance); |
| | 1884 | err = record_load_store_access(env, instance, at_in[i], idx); |
| | 1885 | if (err) |
| | 1886 | goto err_free; |
| | 1887 | |
| | 1888 | if (insn->code == (BPF_JMP | BPF_CALL)) { |
| | 1889 | err = record_call_access(env, instance, at_in[i], idx); |
| | 1890 | if (err) |
| | 1891 | goto err_free; |
| | 1892 | } |
| | 1893 | |
| | 1894 | if (bpf_pseudo_call(insn) || bpf_calls_callback(env, idx)) { |
| | 1895 | kvfree(env->callsite_at_stack[idx]); |
| | 1896 | env->callsite_at_stack[idx] = |
| | 1897 | kvmalloc_objs(*env->callsite_at_stack[idx], |
| | 1898 | MAX_ARG_SPILL_SLOTS, GFP_KERNEL_ACCOUNT); |
| | 1899 | if (!env->callsite_at_stack[idx]) { |
| | 1900 | err = -ENOMEM; |
| | 1901 | goto err_free; |
| | 1902 | } |
| | 1903 | memcpy(env->callsite_at_stack[idx], |
| | 1904 | at_stack_in[i], sizeof(struct arg_track) * MAX_ARG_SPILL_SLOTS); |
| | 1905 | } |
| | 1906 | err = commit_stack_write_marks(env, instance, idx); |
| | 1907 | if (err) |
| | 1908 | goto err_free; |
| | 1909 | } |
| | 1910 | |
| 1668 | info->at_in = at_in; | 1911 | info->at_in = at_in; |
| 1669 | at_in = NULL; | 1912 | at_in = NULL; |
| 1670 | info->len = len; | 1913 | info->len = len; |