• Nils Goroll's avatar
    Support dynamic SUBs with VRT_call() / VRT_check_call() · ec4e6617
    Nils Goroll authored
    This allows for VCL subs to be called dynamically at runtime: vmods
    may store references to VCL_SUBs and call them some time later.
    
    Calling a VCL_SUB pointer requires to conduct two fundamental checks
    also at runtime, which previously were implemented by VCC at compile
    time only:
    
    * checking the allowed context
    * checking for recursive calls
    
    The foundation for both has been laid in previous commits and has
    been made available through VPI_Call_*().
    
    Note that we do not change the VCC checks in any way for static calls,
    we only add runtime checks for dynamic calls.
    
    This commit adds a VRT interface for dynamic calls and changes VGC to
    ensure proper runtime checking (as suggested by phk).
    
    We add to the vcl_func_f signature
    - a vcl_func_call_e argument denoting the type of call and
    - a vcl_func_fail_e value argument to optionally return a failure code
    
    On vcl_func_call_e:
    
    VSUB_CHECK allows runtime callers to preflight a "can this sub be
    called" check, which is the basis for VRT_check_call().
    
    VSUB_DYNAMIC must be used by any calls outside VGC.
    
    VSUB_STATIC is for VGC only and, strictly speaking, would not be
    required. It is added for clarity and safety and used for "call"
    actions from VCL.
    
    On vcl_func_fail_e:
    
    If the argument is present, any error will be returned in
    it. Otherwise, the generated code fails the VCL.
    
    On the implementation:
    
    To minimize the overhead for runtime context and recursion checks, we
    only add them where needed.
    
    In previous commits, we changed the VCC walks over SUBs to ensure that
    any dynamically referenced SUBs and all SUBs reachable from these via
    "call" can be identified by having more references than calls
    (p->sym->nref > p->called).
    
    So we now know, at compile time, for which SUBs we need to add runtime
    checks. Read
    https://github.com/varnishcache/varnish-cache/pull/3163#issuecomment-770266098
    for more details.
    
    Depending on whether the SUB is dynamically callable or not, we either
    add runtime checks or keep the code identical to before (except for a
    changed signature and some assertions).
    
    For the dynamic case, we need to wrap the actual SUB in order to clear
    the recursion check bit when it returns. Note that this happens
    independend of whether the actuall call is static or dynamic - because
    it has to. The recursion check for dynamic calls requires any previous
    static calls to be registered.
    
    The VGC for the dynamic case looks like this (slightly edited for
    clarity):
    
    static void
    VGC_function_foo_checked(VRT_CTX)
    {
      assert(ctx->method & (VCL_MET_DELIVER|VCL_MET_SYNTH));
      { { VPI_count(ctx, 1);
          // ... as before
    
    void v_matchproto_(vcl_func_f)
    VGC_function_foo(VRT_CTX, enum vcl_func_call_e call,
        enum vcl_func_fail_e *failp)
    {
      enum vcl_func_fail_e fail;
    
      fail = VPI_Call_Check(ctx, &VCL_conf, 0x300, 0);
      if (failp)
        *failp = fail;
      else if (fail == VSUB_E_METHOD)
        VRT_fail(ctx, "call to \"sub foo{}\" not allowed from here");
      else if (fail == VSUB_E_RECURSE)
        VRT_fail(ctx, "Recursive call to \"sub foo{}\" not allowed from here");
      else
        assert(fail == VSUB_E_OK);
    
      if (fail != VSUB_E_OK || call == VSUB_CHECK)
        return;
      VPI_Call_Begin(ctx, 0);
      VGC_function_foo_checked(ctx);
      VPI_Call_End(ctx, 0);
    }
    
    The actual function body remains unchanged, but is contained in the
    static function named "*_checked". The vcl_func_f is now a wrapper.
    
    The wrapper
    
    - checks for context errors using VPI_Call_Check()
    - either returns any error in the failp argument or fails the VCL
    - encoses the call to the "_checked" function with VPI_Call_Begin /
      VPI_Call_End which set/clear the recursion marker bit.
    ec4e6617
Name
Last commit
Last update
..
compat Loading commit data...
tbl Loading commit data...
vapi Loading commit data...
Makefile.am Loading commit data...
generate.py Loading commit data...
libvcc.h Loading commit data...
miniobj.h Loading commit data...
vas.h Loading commit data...
vav.h Loading commit data...
vbh.h Loading commit data...
vbm.h Loading commit data...
vbm_test.c Loading commit data...
vcc_interface.h Loading commit data...
vcli.h Loading commit data...
vcli_serve.h Loading commit data...
vcs.h Loading commit data...
vct.h Loading commit data...
vcurses.h Loading commit data...
vdef.h Loading commit data...
venc.h Loading commit data...
vend.h Loading commit data...
vev.h Loading commit data...
vfil.h Loading commit data...
vfl.h Loading commit data...
vin.h Loading commit data...
vjsn.h Loading commit data...
vlu.h Loading commit data...
vmb.h Loading commit data...
vnum.h Loading commit data...
vpf.h Loading commit data...
vqueue.h Loading commit data...
vre.h Loading commit data...
vrnd.h Loading commit data...
vrt.h Loading commit data...
vsa.h Loading commit data...
vsb.h Loading commit data...
vsc_priv.h Loading commit data...
vsha256.h Loading commit data...
vsl_priv.h Loading commit data...
vsm_priv.h Loading commit data...
vss.h Loading commit data...
vsub.h Loading commit data...
vtcp.h Loading commit data...
vtim.h Loading commit data...
vtree.h Loading commit data...
vus.h Loading commit data...
vut.h Loading commit data...
vut_options.h Loading commit data...