• Dave Marchevsky's avatar
    bpf: Migrate release_on_unlock logic to non-owning ref semantics · 6a3cd331
    Dave Marchevsky authored
    This patch introduces non-owning reference semantics to the verifier,
    specifically linked_list API kfunc handling. release_on_unlock logic for
    refs is refactored - with small functional changes - to implement these
    semantics, and bpf_list_push_{front,back} are migrated to use them.
    
    When a list node is pushed to a list, the program still has a pointer to
    the node:
    
      n = bpf_obj_new(typeof(*n));
    
      bpf_spin_lock(&l);
      bpf_list_push_back(&l, n);
      /* n still points to the just-added node */
      bpf_spin_unlock(&l);
    
    What the verifier considers n to be after the push, and thus what can be
    done with n, are changed by this patch.
    
    Common properties both before/after this patch:
      * After push, n is only a valid reference to the node until end of
        critical section
      * After push, n cannot be pushed to any list
      * After push, the program can read the node's fields using n
    
    Before:
      * After push, n retains the ref_obj_id which it received on
        bpf_obj_new, but the associated bpf_reference_state's
        release_on_unlock field is set to true
        * release_on_unlock field and associated logic is used to implement
          "n is only a valid ref until end of critical section"
      * After push, n cannot be written to, the node must be removed from
        the list before writing to its fields
      * After push, n is marked PTR_UNTRUSTED
    
    After:
      * After push, n's ref is released and ref_obj_id set to 0. NON_OWN_REF
        type flag is added to reg's type, indicating that it's a non-owning
        reference.
        * NON_OWN_REF flag and logic is used to implement "n is only a
          valid ref until end of critical section"
      * n can be written to (except for special fields e.g. bpf_list_node,
        timer, ...)
    
    Summary of specific implementation changes to achieve the above:
    
      * release_on_unlock field, ref_set_release_on_unlock helper, and logic
        to "release on unlock" based on that field are removed
    
      * The anonymous active_lock struct used by bpf_verifier_state is
        pulled out into a named struct bpf_active_lock.
    
      * NON_OWN_REF type flag is introduced along with verifier logic
        changes to handle non-owning refs
    
      * Helpers are added to use NON_OWN_REF flag to implement non-owning
        ref semantics as described above
        * invalidate_non_owning_refs - helper to clobber all non-owning refs
          matching a particular bpf_active_lock identity. Replaces
          release_on_unlock logic in process_spin_lock.
        * ref_set_non_owning - set NON_OWN_REF type flag after doing some
          sanity checking
        * ref_convert_owning_non_owning - convert owning reference w/
          specified ref_obj_id to non-owning references. Set NON_OWN_REF
          flag for each reg with that ref_obj_id and 0-out its ref_obj_id
    
      * Update linked_list selftests to account for minor semantic
        differences introduced by this patch
        * Writes to a release_on_unlock node ref are not allowed, while
          writes to non-owning reference pointees are. As a result the
          linked_list "write after push" failure tests are no longer scenarios
          that should fail.
        * The test##missing_lock##op and test##incorrect_lock##op
          macro-generated failure tests need to have a valid node argument in
          order to have the same error output as before. Otherwise
          verification will fail early and the expected error output won't be seen.
    Signed-off-by: default avatarDave Marchevsky <davemarchevsky@fb.com>
    Link: https://lore.kernel.org/r/20230212092715.1422619-2-davemarchevsky@fb.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
    6a3cd331
verifier.c 508 KB