[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