[master] 33d09811d vcc_vmod: implement $Restrict feature
Nils Goroll
nils.goroll at uplex.de
Mon Apr 24 13:46:08 UTC 2023
commit 33d09811dfe3cc597bf9207abc9af7b4dca36a79
Author: Walid Boudebouda <walid.boudebouda at gmail.com>
Date: Fri Mar 17 17:39:18 2023 +0100
vcc_vmod: implement $Restrict feature
This commit implements the $Restrict feature described in
https://github.com/varnishcache/varnish-cache/wiki/VIP4%3A-Restrict-VMOD-function-call-sites
it offers the ability to restrict vmod functions and methods scope so that they can only be called
from limited VCL call sites
diff --git a/.gitignore b/.gitignore
index 7432b1b94..5762b8a9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ cscope.*out
/include/vmod_abi.h
/include/tbl/vcl_returns.h
/include/tbl/vrt_stv_var.h
+/include/tbl/vcl_context.h
/include/vcs_version.h
/lib/libvcc/vcc_fixed_token.c
/lib/libvcc/vcc_obj.c
diff --git a/bin/varnishtest/tests/m00055.vtc b/bin/varnishtest/tests/m00055.vtc
new file mode 100644
index 000000000..39172c16c
--- /dev/null
+++ b/bin/varnishtest/tests/m00055.vtc
@@ -0,0 +1,59 @@
+varnishtest "Test $Restrict scope"
+
+feature topbuild
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+
+varnish v1 -arg "-pvmod_path=${tmpdir}" -vcl+backend {} -start
+
+
+filewrite ${tmpdir}/libvmod_wrong.so "VMOD_JSON_SPEC\x02"
+filewrite -a ${tmpdir}/libvmod_wrong.so {
+[
+ [
+ "$VMOD",
+ "1.0",
+ "wrong",
+ "Vmod_vmod_wrong_Func",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "17",
+ "0"
+ ],
+ [
+ "$CPROTO",
+ "struct Vmod_vmod_wrong_Func {",
+ "",
+ "}"
+ ],
+ [
+ "$FUNC",
+ "test",
+ [
+ [
+ "VOID"
+ ],
+ "Vmod_vmod_wrong_Func.test",
+ ""
+ ]
+ ],
+ [
+ "$RESTRICT",
+ [
+ "vcl_recv",
+ "foo",
+ "deliver"
+ ]
+ ]
+]
+}
+
+filewrite -a ${tmpdir}/libvmod_wrong.so "\x03"
+
+varnish v1 -errvcl {invalid scope for $Restrict: foo} { import wrong; }
+
+shell "rm -f ${tmpdir}/libvmod_wrong.so ${tmpdir}wrong.vcl"
diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst
index a4437396d..1273277b2 100644
--- a/doc/sphinx/reference/vmod.rst
+++ b/doc/sphinx/reference/vmod.rst
@@ -264,6 +264,19 @@ managing instances, in particular their memory management. As the
lifetime of object instances is the vcl, they will usually be
allocated from the heap.
+Functions and Methods scope restriction
+---------------------------------------
+
+The ``$Restrict`` stanza offers a way to limit the scope of the preceding vmod function
+or method, so that they can only be called from restricted vcl call sites.
+It must only appear after a ``$Method`` or ``$Function`` and has the following syntax::
+
+ $Restrict scope1 [scope2 ...]
+
+Possible scope values are:
+backend, client, housekeeping, recv, pipe, pass, hash, purge, miss, hit,
+deliver, synth, backend_fetch, backend_response, backend_error, init, fini
+
Deprecated Aliases
------------------
diff --git a/include/Makefile.am b/include/Makefile.am
index 0db210f5b..0fa196b39 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -35,6 +35,7 @@ nobase_pkginclude_HEADERS = \
tbl/symbol_kind.h \
tbl/vcc_feature_bits.h \
tbl/vcl_returns.h \
+ tbl/vcl_context.h \
tbl/vcl_states.h \
tbl/vhd_fsm.h \
tbl/vhd_fsm_funcs.h \
diff --git a/lib/libvcc/generate.py b/lib/libvcc/generate.py
index 2199f6a07..84790e347 100755
--- a/lib/libvcc/generate.py
+++ b/lib/libvcc/generate.py
@@ -708,6 +708,20 @@ fo.close()
#######################################################################
+fo = open(join(buildroot, "include/tbl/vcl_context.h"), "w")
+file_header(fo)
+
+for i in returns:
+ fo.write("\nVCL_CTX(vcl_%s,%s)" % (i[0],i[0].upper()))
+fo.write("\nVCL_CTX(backend, TASK_B)")
+fo.write("\nVCL_CTX(client, TASK_C)")
+fo.write("\nVCL_CTX(housekeeping, TASK_H)")
+fo.write("\n")
+fo.write("\n#undef VCL_CTX")
+fo.write("\n")
+fo.close()
+
+#######################################################################
def restrict(fo, spec):
d = dict()
diff --git a/lib/libvcc/vcc_vmod.c b/lib/libvcc/vcc_vmod.c
index b4804cfff..e998da061 100644
--- a/lib/libvcc/vcc_vmod.c
+++ b/lib/libvcc/vcc_vmod.c
@@ -547,6 +547,7 @@ vcc_ParseImport(struct vcc *tl)
msym->import = vsym->import;
msym->vmod_name = vsym->vmod_name;
vcc_VmodSymbols(tl, msym);
+ AZ(tl->err);
// XXX: insert msym in sideways ?
vcc_vim_destroy(&vim);
return;
@@ -559,6 +560,7 @@ vcc_ParseImport(struct vcc *tl)
msym->import = vim;
msym->vmod_name = TlDup(tl, vim->name);
vcc_VmodSymbols(tl, msym);
+ ERRCHK(tl);
vcc_emit_setup(tl, vim);
}
diff --git a/lib/libvcc/vcc_vmod.h b/lib/libvcc/vcc_vmod.h
index 1fe6e5360..a1084c2f5 100644
--- a/lib/libvcc/vcc_vmod.h
+++ b/lib/libvcc/vcc_vmod.h
@@ -35,6 +35,7 @@
STANZA(FUNC, func, SYM_FUNC) \
STANZA(METHOD, method, SYM_METHOD) \
STANZA(OBJ, obj, SYM_OBJECT) \
- STANZA(VMOD, vmod, SYM_NONE)
+ STANZA(VMOD, vmod, SYM_NONE) \
+ STANZA(RESTRICT, restrict, SYM_NONE)
void vcc_VmodSymbols(struct vcc *tl, const struct symbol *sym);
diff --git a/lib/libvcc/vcc_vmod_sym.c b/lib/libvcc/vcc_vmod_sym.c
index ee30197b9..2b585ef00 100644
--- a/lib/libvcc/vcc_vmod_sym.c
+++ b/lib/libvcc/vcc_vmod_sym.c
@@ -107,9 +107,49 @@ alias_sym(struct vcc *tl, const struct symbol *psym, const struct vjsn_val *v)
free(func);
}
+static void
+func_restrict(struct vcc *tl, struct symbol *sym, vcc_kind_t kind, const struct vjsn_val *v)
+{
+ struct vjsn_val *vv;
+
+ AN(v);
+ AN(sym);
+
+ if (kind != SYM_FUNC && kind != SYM_METHOD)
+ return;
+
+ v = VTAILQ_NEXT(v, list);
+ if (!v || !vjsn_is_array(v))
+ return;
+ vv = VTAILQ_FIRST(&v->children);
+ AN(vv);
+ assert(vjsn_is_string(vv));
+ if (strcmp(vv->value, "$RESTRICT"))
+ return;
+ vv = VTAILQ_NEXT(vv, list);
+ AN(vv);
+ assert(vjsn_is_array(vv));
+ sym->r_methods = 0;
+ vv = VTAILQ_FIRST(&vv->children);
+ unsigned s;
+ while (vv) {
+ s = 0;
+ #define VCL_CTX(l,H) \
+ if (strcmp(vv->value,#l) == 0) s = VCL_MET_##H;
+ #include "tbl/vcl_context.h"
+ if (!s) {
+ VSB_printf(tl->sb, "Error in vmod \"%s\", invalid scope for $Restrict: %s\n",sym->vmod_name, vv->value);
+ tl->err = 1;
+ break;
+ }
+ sym->r_methods |= s;
+ vv = VTAILQ_NEXT(vv,list);
+ }
+}
+
static void
func_sym(struct vcc *tl, vcc_kind_t kind, const struct symbol *psym,
- const struct vjsn_val *v)
+ const struct vjsn_val *v, const struct vjsn_val *vv)
{
struct symbol *sym;
struct vsb *buf;
@@ -155,6 +195,7 @@ func_sym(struct vcc *tl, vcc_kind_t kind, const struct symbol *psym,
sym->type = VCC_Type(v->value);
AN(sym->type);
sym->r_methods = VCL_MET_TASK_ALL;
+ func_restrict(tl, sym, kind, vv);
}
void
@@ -188,8 +229,10 @@ vcc_VmodSymbols(struct vcc *tl, const struct symbol *sym)
#define STANZA(UU, ll, ss) if (!strcmp(vv1->value, "$" #UU)) kind = ss;
STANZA_TBL
#undef STANZA
- if (kind != SYM_NONE)
- func_sym(tl, kind, sym, vv2);
+ if (kind != SYM_NONE) {
+ func_sym(tl, kind, sym, vv2, vv);
+ ERRCHK(tl);
+ }
}
}
diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py
index 8f102d2cd..270e64cb5 100755
--- a/lib/libvcc/vmodtool.py
+++ b/lib/libvcc/vmodtool.py
@@ -714,6 +714,7 @@ class FunctionStanza(Stanza):
self.proto = ProtoType(self)
self.rstlbl = '%s.%s()' % (self.vcc.modname, self.proto.name)
self.vcc.contents.append(self)
+ self.restrict = None
def cstuff(self, fo, where):
fo.write(self.proto.cproto(['VRT_CTX'], where))
@@ -721,9 +722,17 @@ class FunctionStanza(Stanza):
def cstruct(self, fo, define):
self.fmt_cstruct_proto(fo, self.proto, define)
+ def rstdoc(self, fo, unused_man):
+ super().rstdoc(fo,unused_man)
+ if (self.restrict is not None):
+ fo.write("\nRestricted to: ``%s``\n\n" % ', '.join(self.restrict.restrict_toks))
+ self.restrict.rstdoc(fo, unused_man)
+
def json(self, jl):
jl.append(["$FUNC", "%s" % self.proto.name])
self.proto.jsonproto(jl[-1], self.proto.cname())
+ if (self.restrict is not None):
+ self.restrict.json(jl)
class ObjectStanza(Stanza):
@@ -830,13 +839,47 @@ class MethodStanza(Stanza):
self.proto.obj = "x" + self.pfx
self.rstlbl = 'x%s()' % self.proto.name
p.methods.append(self)
+ self.restrict = None
def cstruct(self, fo, define):
self.fmt_cstruct_proto(fo, self.proto, define)
+ def rstdoc(self, fo, unused_man):
+ super().rstdoc(fo,unused_man)
+ if (self.restrict is not None):
+ fo.write("\nRestricted to: ``%s``\n\n" % ', '.join(self.restrict.restrict_toks))
+ self.restrict.rstdoc(fo, unused_man)
+
def json(self, jl):
jl.append(["$METHOD", self.proto.name[len(self.pfx)+1:]])
self.proto.jsonproto(jl[-1], self.proto.cname())
+ if (self.restrict is not None):
+ self.restrict.json(jl)
+
+
+class RestrictStanza(Stanza):
+
+ ''' $Restrict scope1 [scope2 ..] '''
+
+ def parse(self):
+ if len(self.toks) < 2:
+ self.syntax()
+ p = self.vcc.contents[-1]
+ if (isinstance(p, ObjectStanza)):
+ if(p.methods):
+ p.methods[-1].restrict = self
+ else:
+ err("$Restrict should be after $Method or $Function", False)
+ elif (isinstance(p, FunctionStanza)):
+ p.restrict = self
+ else :
+ err("$Restrict should be after $Method or $Function", False)
+ self.restrict_toks = self.toks[1:]
+
+ def json(self, jl):
+ tab = ["$RESTRICT"]
+ tab.append(self.restrict_toks)
+ jl.append(tab)
class AliasStanza(Stanza):
@@ -898,6 +941,7 @@ DISPATCH = {
"Method": MethodStanza,
"Synopsis": SynopsisStanza,
"Alias": AliasStanza,
+ "Restrict": RestrictStanza,
}
More information about the varnish-commit
mailing list