Skip to content

VIP4: Restrict VMOD function call sites

Dridi Boukelmoune edited this page Mar 23, 2021 · 7 revisions

Synopsis

Teach the VCC to conditionally refuse $Function or $Method calls.

See also VIP9B $Scope

Why?

It started with the fact that VRT functions sometimes expect to be called under certain conditions but nothing really documents or enforces such conditions. These functions could at least assert that their requirements are met if that is not already the case, but this is outside of this VIP's scope.

VMOD functions defined by the ad-hoc VCC can however express this kind of requirements when that makes sense. Besides VMOD constructs like the $Event function that are not visible in VCL or $Object constructors that can only be used in vcl_init all VMOD functions can be called from anywhere.

If a VMOD function calls a VRT function that has a special requirement, the VMOD author could document it, and the VCC could enforce it.

How?

A new $Restrict token followed by a list of blank-separated restrictions. There's already such a mechanism in generate.py and general consensus leans towards reusing the same parameters:

  • client (all client subroutines)
  • backend (all backend subroutines)
  • <subroutine> (a specific subroutine)

Example:

$Function VOID needs_a_bereq(...)
$Restrict backend pipe

Another example:

$Module mtstatus 3 Mean time status for Varnish Cache
$Event event_function

# Both functions use VRT_synth_page
$Function VOID mtstatus(REAL delta)
$Restrict synth

# This function gets the HTML contents from a file, it might be worth caching 
$Function VOID html(STRING file = "/usr/share/vmod-mtstatus/page.html")
$Restrict synth backend_error

Function arguments

Some VCL symbols have a scope restriction and the compiler performs those checks when those symbols are passed to a VMOD:

# $Module debug
# $Function VOID sethdr(HEADER, STRING)

sub vcl_deliver {
        # the direct symbol case
        debug.sethdr(resp.http.foo, "bar");
}

Such checks are typically inapplicable when values come from VMODs:

# $Module strawman
# $Function HEADER get_incompatible_header()

sub vcl_deliver {
        # the indirect vmod case
        debug.sethdr(strawman.get_incompatible_header(), "this will panic: VRT_selecthttp 'where' invalid");
}

One notable exception to the rule above is the SUB type. When a subroutine is currently passed as VMOD function or object method argument, the compiler does not verify that the scope is compatible to enable dynamic calls.

We could enforce compiler checks in both cases by creating a VCC signature to disable those checks:

$Module dispatcher
$Function register(STRING hostname, &SUB subroutine)

$Module looper
$Function foreach(SUB subroutine)

A scope type would gain a compiler check enforcement even in the indirect VMOD case, and a VMOD would have to explicitly signal with the & symbol that the argument may be used from a different call site.

In other words, asking for a scoped TYPE means that the VMOD expects the argument to be safe at the call site, which means that it has to come from a literal symbol since an expression would otherwise lose that information. On the other hand asking for a scoped &TYPE implies that the VMOD is responsible for runtime checks, since compiler checks will not happen.

Clone this wiki locally