git: 086e0149ae56 - main - jail: Use re-entrant versions of lex & yacc, and lex's yylineno

From: Jamie Gritton <jamie_at_FreeBSD.org>
Date: Sun, 04 Jun 2023 03:07:28 UTC
The branch main has been updated by jamie:

URL: https://cgit.FreeBSD.org/src/commit/?id=086e0149ae56641af245ce472e787c2f67d3aea5

commit 086e0149ae56641af245ce472e787c2f67d3aea5
Author:     Jamie Gritton <jamie@FreeBSD.org>
AuthorDate: 2023-06-04 03:07:09 +0000
Commit:     Jamie Gritton <jamie@FreeBSD.org>
CommitDate: 2023-06-04 03:07:09 +0000

    jail: Use re-entrant versions of lex & yacc, and lex's yylineno
---
 usr.sbin/jail/config.c    | 27 ++++++++++-------
 usr.sbin/jail/jail.c      |  8 ++---
 usr.sbin/jail/jaillex.l   | 76 ++++++++++++++++++-----------------------------
 usr.sbin/jail/jailp.h     | 11 +++----
 usr.sbin/jail/jailparse.y | 27 +++++++++++++++++
 5 files changed, 83 insertions(+), 66 deletions(-)

diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c
index cbf5859b1329..52e1cbf05c28 100644
--- a/usr.sbin/jail/config.c
+++ b/usr.sbin/jail/config.c
@@ -51,10 +51,10 @@ struct ipspec {
 	unsigned	flags;
 };
 
-extern FILE *yyin;
-extern int yynerrs;
-
-extern int yyparse(void);
+extern int yylex_init_extra(struct cflex *extra, void *scanner);
+extern int yylex_destroy(void *scanner);
+extern int yyparse(void *scanner);
+extern int yyset_in(FILE *fp, void *scanner);
 
 struct cfjails cfjails = TAILQ_HEAD_INITIALIZER(cfjails);
 
@@ -127,7 +127,7 @@ static const struct ipspec intparams[] = {
  * Parse the jail configuration file.
  */
 void
-load_config(void)
+load_config(const char *cfname)
 {
 	struct cfjails wild;
 	struct cfparams opp;
@@ -135,19 +135,26 @@ load_config(void)
 	struct cfparam *p, *vp, *tp;
 	struct cfstring *s, *vs, *ns;
 	struct cfvar *v, *vv;
+	struct cflex cflex;
 	char *ep;
+	void *scanner;
 	int did_self, jseq, pgen;
 
+	cflex.cfname = cfname;
+	cflex.error = 0;
+	yylex_init_extra(&cflex, &scanner);
 	if (!strcmp(cfname, "-")) {
-		cfname = "STDIN";
-		yyin = stdin;
+		cflex.cfname = "STDIN";
+		yyset_in(stdin, scanner);
 	} else {
-		yyin = fopen(cfname, "r");
-		if (!yyin)
+		FILE *yfp = fopen(cfname, "r");
+		if (!yfp)
 			err(1, "%s", cfname);
+		yyset_in(yfp, scanner);
 	}
-	if (yyparse() || yynerrs)
+	if (yyparse(scanner) || cflex.error)
 		exit(1);
+	yylex_destroy(scanner);
 
 	/* Separate the wildcard jails out from the actual jails. */
 	jseq = 0;
diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c
index 02eb957a2d45..a2c725d51b70 100644
--- a/usr.sbin/jail/jail.c
+++ b/usr.sbin/jail/jail.c
@@ -56,7 +56,6 @@ struct permspec {
 	int		rev;
 };
 
-const char *cfname;
 int iflag;
 int note_remove;
 int verbose;
@@ -138,6 +137,7 @@ main(int argc, char **argv)
 	FILE *jfp;
 	struct cfjail *j;
 	char *JidFile;
+	const char *cfname;
 	size_t sysvallen;
 	unsigned op, pi;
 	int ch, docf, error, i, oldcl, sysval;
@@ -287,7 +287,7 @@ main(int argc, char **argv)
 	} else if (op == JF_STOP || op == JF_SHOW) {
 		/* Just print list of all configured non-wildcard jails */
 		if (op == JF_SHOW) {
-			load_config();
+			load_config(cfname);
 			show_jails();
 			exit(0);
 		}
@@ -300,7 +300,7 @@ main(int argc, char **argv)
 					usage();
 		if ((docf = !Rflag &&
 		     (!strcmp(cfname, "-") || stat(cfname, &st) == 0)))
-			load_config();
+			load_config(cfname);
 		note_remove = docf || argc > 1 || wild_jail_name(argv[0]);
 	} else if (argc > 1 || (argc == 1 && strchr(argv[0], '='))) {
 		/* Single jail specified on the command line */
@@ -348,7 +348,7 @@ main(int argc, char **argv)
 		/* From the config file, perhaps with a specified jail */
 		if (Rflag || !docf)
 			usage();
-		load_config();
+		load_config(cfname);
 	}
 
 	/* Find out which jails will be run. */
diff --git a/usr.sbin/jail/jaillex.l b/usr.sbin/jail/jaillex.l
index 1dcf5bef5358..540b43ab3c5c 100644
--- a/usr.sbin/jail/jaillex.l
+++ b/usr.sbin/jail/jaillex.l
@@ -38,40 +38,32 @@ __FBSDID("$FreeBSD$");
 #include "jailp.h"
 #include "y.tab.h"
 
-extern int yynerrs;
+#define YY_DECL int yylex(YYSTYPE *yylval, yyscan_t yyscanner)
+#define YY_EXTRA_TYPE struct cflex*
 
-static ssize_t text2lval(size_t triml, size_t trimr, int tovar);
+extern YY_DECL;
 
-static int instr;
-static int lineno = 1;
+static ssize_t text2lval(size_t triml, size_t trimr, int tovar,
+    YYSTYPE *yylval, yyscan_t scanner);
 
-#define YY_DECL int yylex(void)
+static int instr;
 %}
 
 %option noyywrap
 %option noinput
 %option nounput
+%option reentrant
+%option yylineno
 
 %start _ DQ
 
 %%
 
 			/* Whitespace or equivalent */
-<_>[ \t]+		instr = 0;
-<_>#.*			;
-<_>\/\/.*		;
-<_>\/\*([^*]|(\*+([^*\/])))*\*+\/ {
-				const char *s;
-
-				for (s = yytext; s < yytext + yyleng; s++)
-					if (*s == '\n')
-						lineno++;
-				instr = 0;
-			}
-<_>\n			{
-				lineno++;
-				instr = 0;
-			}
+<_>[ \t\r\n]+		instr = 0;
+<_>#.*			instr = 0;
+<_>\/\/.*		instr = 0;
+<_>\/\*([^*]|(\*+([^*\/])))*\*+\/ instr = 0;
 
 			/* Reserved tokens */
 <_>\+=			{
@@ -87,13 +79,13 @@ static int lineno = 1;
 <_,DQ>[A-Za-z0-9_!%&()\-.:<>?@\[\]^`|~]+ |
 <_,DQ>\\(.|\n|[0-7]{1,3}|x[0-9A-Fa-f]{1,2}) |
 <_,DQ>[$*+/\\]		{
-				(void)text2lval(0, 0, 0);
+				(void)text2lval(0, 0, 0, yylval, yyscanner);
 				return instr ? STR1 : (instr = 1, STR);
 			}
 
 			/* Single and double quoted strings */
 <_>'([^\'\\]|\\(.|\n))*' {
-				(void)text2lval(1, 1, 0);
+				(void)text2lval(1, 1, 0, yylval, yyscanner);
 				return instr ? STR1 : (instr = 1, STR);
 			}
 <_>\"([^"\\]|\\(.|\n))*\" |
@@ -102,7 +94,8 @@ static int lineno = 1;
 				ssize_t atvar;
 
 				skip = yytext[0] == '"' ? 1 : 0;
-				atvar = text2lval(skip, 1, 1);
+				atvar = text2lval(skip, 1, 1, yylval,
+					yyscanner);
 				if (atvar < 0)
 					BEGIN _;
 				else {
@@ -120,32 +113,32 @@ static int lineno = 1;
 
 			/* Variables, single-word or bracketed */
 <_,DQ>$[A-Za-z_][A-Za-z_0-9]* {
-				(void)text2lval(1, 0, 0);
+				(void)text2lval(1, 0, 0, yylval, yyscanner);
 				return instr ? VAR1 : (instr = 1, VAR);
 			}
 <_>$\{([^\n{}]|\\(.|\n))*\} |
 <DQ>$\{([^\n\"{}]|\\(.|\n))*\} {
-				(void)text2lval(2, 1, 0);
+				(void)text2lval(2, 1, 0, yylval, yyscanner);
 				return instr ? VAR1 : (instr = 1, VAR);
 			}
 
 			/* Partially formed bits worth complaining about */
 <_>\/\*([^*]|(\*+([^*\/])))*\** {
 				warnx("%s line %d: unterminated comment",
-				    cfname, lineno);
-				yynerrs++;
+				    yyextra->cfname, yylineno);
+				yyextra->error = 1;
 			}
 <_>'([^\n'\\]|\\.)*	|
 <_>\"([^\n\"\\]|\\.)*	{
 				warnx("%s line %d: unterminated string",
-				    cfname, lineno);
-				yynerrs++;
+				    yyextra->cfname, yylineno);
+				yyextra->error = 1;
 			}
 <_>$\{([^\n{}]|\\.)*	|
 <DQ>$\{([^\n\"{}]|\\.)*	{
 				warnx("%s line %d: unterminated variable",
-				    cfname, lineno);
-				yynerrs++;
+				    yyextra->cfname, yylineno);
+				yyextra->error = 1;
 			}
 
 			/* A hack because "<0>" rules aren't allowed */
@@ -157,28 +150,19 @@ static int lineno = 1;
 
 %%
 
-void
-yyerror(const char *s)
-{
-	if (!yytext)
-		warnx("%s line %d: %s", cfname, lineno, s);
-	else if (!yytext[0])
-		warnx("%s: unexpected EOF", cfname);
-	else
-		warnx("%s line %d: %s: %s", cfname, lineno, yytext, s);
-}
-
 /*
  * Copy string from yytext to yylval, handling backslash escapes,
  * and optionally stopping at the beginning of a variable.
  */
 static ssize_t
-text2lval(size_t triml, size_t trimr, int tovar)
+text2lval(size_t triml, size_t trimr, int tovar, YYSTYPE *yylval,
+    yyscan_t scanner)
 {
 	char *d;
 	const char *s, *se;
 
-	yylval.cs = d = emalloc(yyleng - trimr - triml + 1);
+	struct yyguts_t *yyg = scanner;
+	yylval->cs = d = emalloc(yyleng - trimr - triml + 1);
 	se = yytext + (yyleng - trimr);
 	for (s = yytext + triml; s < se; s++, d++) {
 		if (*s != '\\') {
@@ -186,8 +170,6 @@ text2lval(size_t triml, size_t trimr, int tovar)
 				*d = '\0';
 				return s - yytext;
 			}
-			if (*s == '\n')
-				lineno++;
 			*d = *s;
 			continue;
 		}
@@ -209,8 +191,8 @@ text2lval(size_t triml, size_t trimr, int tovar)
 		case 'r':	*d = '\r';	break;
 		case 't':	*d = '\t';	break;
 		case 'v':	*d = '\v';	break;
-		case '\n':	d--; lineno++;	break;
 		default:	*d = *s;	break;
+		case '\n':	d--;		break;
 		case 'x':
 			*d = 0;
 			if (s + 1 >= se)
diff --git a/usr.sbin/jail/jailp.h b/usr.sbin/jail/jailp.h
index 0325c2a101a4..6ea4e3ec09a3 100644
--- a/usr.sbin/jail/jailp.h
+++ b/usr.sbin/jail/jailp.h
@@ -197,6 +197,11 @@ struct cfdepend {
 	unsigned		flags;
 };
 
+struct cflex {
+	const char		*cfname;
+	int			error;
+};
+
 extern void *emalloc(size_t);
 extern void *erealloc(void *, size_t);
 extern char *estrdup(const char *);
@@ -209,7 +214,7 @@ extern int next_command(struct cfjail *j);
 extern int finish_command(struct cfjail *j);
 extern struct cfjail *next_proc(int nonblock);
 
-extern void load_config(void);
+extern void load_config(const char *cfname);
 extern struct cfjail *add_jail(void);
 extern void add_param(struct cfjail *j, const struct cfparam *p,
     enum intparam ipnum, const char *value);
@@ -232,13 +237,9 @@ extern int start_state(const char *target, int docf, unsigned state,
 extern void requeue(struct cfjail *j, struct cfjails *queue);
 extern void requeue_head(struct cfjail *j, struct cfjails *queue);
 
-extern void yyerror(const char *);
-extern int yylex(void);
-
 extern struct cfjails cfjails;
 extern struct cfjails ready;
 extern struct cfjails depend;
-extern const char *cfname;
 extern int iflag;
 extern int note_remove;
 extern int paralimit;
diff --git a/usr.sbin/jail/jailparse.y b/usr.sbin/jail/jailparse.y
index 44e2aacb457e..ccc311a76223 100644
--- a/usr.sbin/jail/jailparse.y
+++ b/usr.sbin/jail/jailparse.y
@@ -30,6 +30,7 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include <err.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -57,6 +58,11 @@ static struct cfjail *global_jail;
 %type <ss> value
 %type <s>  string
 
+%pure-parser
+
+%lex-param { void *scanner }
+%parse-param { void *scanner }
+
 %%
 
 /*
@@ -221,3 +227,24 @@ string	: STR
 	;
 
 %%
+
+extern int YYLEX_DECL();
+
+extern struct cflex *yyget_extra(void *scanner);
+extern int yyget_lineno(void *scanner);
+extern char *yyget_text(void *scanner);
+
+static void
+YYERROR_DECL()
+{
+	if (!yyget_text(scanner))
+		warnx("%s line %d: %s",
+		    yyget_extra(scanner)->cfname, yyget_lineno(scanner), s);
+	else if (!yyget_text(scanner)[0])
+		warnx("%s: unexpected EOF",
+		    yyget_extra(scanner)->cfname);
+	else
+		warnx("%s line %d: %s: %s",
+		    yyget_extra(scanner)->cfname, yyget_lineno(scanner),
+		    yyget_text(scanner), s);
+}