From 73751be7a5617bfff4a735ae095203a2d3ec50ef Mon Sep 17 00:00:00 2001
From: Martin Gieseking <martin.gieseking@uos.de>
Date: Tue, 22 Mar 2022 15:31:04 +0100
Subject: [PATCH] Prevent writing past CharString memory in compilePassOpcode

Refer: https://github.com/liblouis/liblouis/commit/73751be

---
 liblouis/compileTranslationTable.c | 199 ++++++++++++++++++++---------
 1 file changed, 140 insertions(+), 59 deletions(-)

diff --git a/liblouis/compileTranslationTable.c b/liblouis/compileTranslationTable.c
index 7e4b115..a2ba81e 100644
--- a/liblouis/compileTranslationTable.c
+++ b/liblouis/compileTranslationTable.c
@@ -1640,6 +1640,17 @@ verifyStringOrDots(FileInfo *nested, TranslationTableOpcode opcode, int isString
 	return 0;
 }
 
+static int
+appendInstructionChar(
+		const FileInfo *file, widechar *passInstructions, int *passIC, widechar ch) {
+	if (*passIC >= MAXSTRING) {
+		compileError(file, "multipass operand too long");
+		return 0;
+	}
+	passInstructions[(*passIC)++] = ch;
+	return 1;
+}
+
 static int
 compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 		CharacterClass *characterClasses, TranslationTableOffset *newRuleOffset,
@@ -1684,32 +1695,34 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 	passLine.chars[endTest] = pass_endTest;
 	passLinepos = 0;
 	while (passLinepos <= endTest) {
-		if (passIC >= MAXSTRING) {
-			compileError(passNested, "Test part in multipass operand too long");
-			return 0;
-		}
 		switch ((passSubOp = passLine.chars[passLinepos])) {
 		case pass_lookback:
-			passInstructions[passIC++] = pass_lookback;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_lookback))
+				return 0;
 			passLinepos++;
 			passGetNumber(&passLine, &passLinepos, &passHoldNumber);
 			if (passHoldNumber == 0) passHoldNumber = 1;
-			passInstructions[passIC++] = passHoldNumber;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, passHoldNumber))
+				return 0;
 			break;
 		case pass_not:
-			passInstructions[passIC++] = pass_not;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_not))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_first:
-			passInstructions[passIC++] = pass_first;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_first))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_last:
-			passInstructions[passIC++] = pass_last;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_last))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_search:
-			passInstructions[passIC++] = pass_search;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_search))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_string:
@@ -1717,7 +1730,8 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				return 0;
 			}
 			passLinepos++;
-			passInstructions[passIC++] = pass_string;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_string))
+				return 0;
 			passGetString(&passLine, &passLinepos, &passHoldString, passNested);
 			goto testDoCharsDots;
 		case pass_dots:
@@ -1725,20 +1739,28 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				return 0;
 			}
 			passLinepos++;
-			passInstructions[passIC++] = pass_dots;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_dots))
+				return 0;
 			passGetDots(&passLine, &passLinepos, &passHoldString, passNested);
 		testDoCharsDots:
 			if (passHoldString.length == 0) return 0;
-			passInstructions[passIC++] = passHoldString.length;
+			if (!appendInstructionChar(
+						passNested, passInstructions, &passIC, passHoldString.length))
+				return 0;
 			for (kk = 0; kk < passHoldString.length; kk++)
-				passInstructions[passIC++] = passHoldString.chars[kk];
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldString.chars[kk]))
+					return 0;
 			break;
 		case pass_startReplace:
-			passInstructions[passIC++] = pass_startReplace;
+			if (!appendInstructionChar(
+						passNested, passInstructions, &passIC, pass_startReplace))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_endReplace:
-			passInstructions[passIC++] = pass_endReplace;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_endReplace))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_variable:
@@ -1747,26 +1769,37 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				return 0;
 			switch (passLine.chars[passLinepos]) {
 			case pass_eq:
-				passInstructions[passIC++] = pass_eq;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_eq))
+					return 0;
 				goto doComp;
 			case pass_lt:
 				if (passLine.chars[passLinepos + 1] == pass_eq) {
 					passLinepos++;
-					passInstructions[passIC++] = pass_lteq;
-				} else
-					passInstructions[passIC++] = pass_lt;
+					if (!appendInstructionChar(
+								passNested, passInstructions, &passIC, pass_lteq))
+						return 0;
+				} else if (!appendInstructionChar(
+								   passNested, passInstructions, &passIC, pass_lt))
+					return 0;
 				goto doComp;
 			case pass_gt:
 				if (passLine.chars[passLinepos + 1] == pass_eq) {
 					passLinepos++;
-					passInstructions[passIC++] = pass_gteq;
-				} else
-					passInstructions[passIC++] = pass_gt;
+					if (!appendInstructionChar(
+								passNested, passInstructions, &passIC, pass_gteq))
+						return 0;
+				} else if (!appendInstructionChar(
+								   passNested, passInstructions, &passIC, pass_gt))
+					return 0;
 			doComp:
-				passInstructions[passIC++] = passHoldNumber;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldNumber))
+					return 0;
 				passLinepos++;
 				passGetNumber(&passLine, &passLinepos, &passHoldNumber);
-				passInstructions[passIC++] = passHoldNumber;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldNumber))
+					return 0;
 				break;
 			default:
 				compileError(passNested, "incorrect comparison operator");
@@ -1778,25 +1811,34 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 			if (!passGetAttributes(&passLine, &passLinepos, &passAttributes, passNested))
 				return 0;
 		insertAttributes:
-			passInstructions[passIC++] = pass_attributes;
-			passInstructions[passIC++] = passAttributes >> 16;
-			passInstructions[passIC++] = passAttributes & 0xffff;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_attributes))
+				return 0;
+			if (!appendInstructionChar(
+						passNested, passInstructions, &passIC, (passAttributes >> 16) & 0xffff))
+				return 0;
+			if (!appendInstructionChar(
+						passNested, passInstructions, &passIC, passAttributes & 0xffff))
+				return 0;
 		getRange:
 			if (passLine.chars[passLinepos] == pass_until) {
 				passLinepos++;
-				passInstructions[passIC++] = 1;
-				passInstructions[passIC++] = 0xffff;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, 1)) return 0;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, 0xffff))
+					return 0;
 				break;
 			}
 			passGetNumber(&passLine, &passLinepos, &passHoldNumber);
 			if (passHoldNumber == 0) {
-				passHoldNumber = passInstructions[passIC++] = 1;
-				passInstructions[passIC++] = 1; /* This is not an error */
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, 1)) return 0;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, 1)) return 0;
 				break;
 			}
-			passInstructions[passIC++] = passHoldNumber;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, passHoldNumber))
+				return 0;
 			if (passLine.chars[passLinepos] != pass_hyphen) {
-				passInstructions[passIC++] = passHoldNumber;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldNumber))
+					return 0;
 				break;
 			}
 			passLinepos++;
@@ -1805,7 +1847,8 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				compileError(passNested, "invalid range");
 				return 0;
 			}
-			passInstructions[passIC++] = passHoldNumber;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, passHoldNumber))
+				return 0;
 			break;
 		case pass_groupstart:
 		case pass_groupend:
@@ -1815,9 +1858,14 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 			if (ruleOffset)
 				rule = (TranslationTableRule *)&(*table)->ruleArea[ruleOffset];
 			if (rule && rule->opcode == CTO_Grouping) {
-				passInstructions[passIC++] = passSubOp;
-				passInstructions[passIC++] = ruleOffset >> 16;
-				passInstructions[passIC++] = ruleOffset & 0xffff;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, passSubOp))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset >> 16))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset & 0xffff))
+					return 0;
 				break;
 			} else {
 				compileError(passNested, "%s is not a grouping name",
@@ -1836,16 +1884,22 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				rule = (TranslationTableRule *)&(*table)->ruleArea[ruleOffset];
 			if (rule && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd ||
 								rule->opcode == CTO_SwapDd)) {
-				passInstructions[passIC++] = pass_swap;
-				passInstructions[passIC++] = ruleOffset >> 16;
-				passInstructions[passIC++] = ruleOffset & 0xffff;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_swap))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset >> 16))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset & 0xffff))
+					return 0;
 				goto getRange;
 			}
 			compileError(passNested, "%s is neither a class name nor a swap name.",
 					_lou_showString(&passHoldString.chars[0], passHoldString.length));
 			return 0;
 		case pass_endTest:
-			passInstructions[passIC++] = pass_endTest;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_endTest))
+				return 0;
 			passLinepos++;
 			break;
 		default:
@@ -1870,7 +1924,8 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				return 0;
 			}
 			passLinepos++;
-			passInstructions[passIC++] = pass_string;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_string))
+				return 0;
 			passGetString(&passLine, &passLinepos, &passHoldString, passNested);
 			goto actionDoCharsDots;
 		case pass_dots:
@@ -1879,17 +1934,22 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 			}
 			passLinepos++;
 			passGetDots(&passLine, &passLinepos, &passHoldString, passNested);
-			passInstructions[passIC++] = pass_dots;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_dots))
+				return 0;
 		actionDoCharsDots:
 			if (passHoldString.length == 0) return 0;
-			passInstructions[passIC++] = passHoldString.length;
+			if (!appendInstructionChar(
+						passNested, passInstructions, &passIC, passHoldString.length))
+				return 0;
 			for (kk = 0; kk < passHoldString.length; kk++) {
 				if (passIC >= MAXSTRING) {
 					compileError(passNested,
 							"@ operand in action part of multipass operand too long");
 					return 0;
 				}
-				passInstructions[passIC++] = passHoldString.chars[kk];
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldString.chars[kk]))
+					return 0;
 			}
 			break;
 		case pass_variable:
@@ -1898,16 +1958,25 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				return 0;
 			switch (passLine.chars[passLinepos]) {
 			case pass_eq:
-				passInstructions[passIC++] = pass_eq;
-				passInstructions[passIC++] = passHoldNumber;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_eq))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldNumber))
+					return 0;
 				passLinepos++;
 				passGetNumber(&passLine, &passLinepos, &passHoldNumber);
-				passInstructions[passIC++] = passHoldNumber;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldNumber))
+					return 0;
 				break;
 			case pass_plus:
 			case pass_hyphen:
-				passInstructions[passIC++] = passLine.chars[passLinepos++];
-				passInstructions[passIC++] = passHoldNumber;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC,
+							passLine.chars[passLinepos++]))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, passHoldNumber))
+					return 0;
 				break;
 			default:
 				compileError(passNested, "incorrect variable operator in action part");
@@ -1915,11 +1984,13 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 			}
 			break;
 		case pass_copy:
-			passInstructions[passIC++] = pass_copy;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_copy))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_omit:
-			passInstructions[passIC++] = pass_omit;
+			if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_omit))
+				return 0;
 			passLinepos++;
 			break;
 		case pass_groupreplace:
@@ -1931,9 +2002,14 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 			if (ruleOffset)
 				rule = (TranslationTableRule *)&(*table)->ruleArea[ruleOffset];
 			if (rule && rule->opcode == CTO_Grouping) {
-				passInstructions[passIC++] = passSubOp;
-				passInstructions[passIC++] = ruleOffset >> 16;
-				passInstructions[passIC++] = ruleOffset & 0xffff;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, passSubOp))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset >> 16))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset & 0xffff))
+					return 0;
 				break;
 			}
 			compileError(passNested, "%s is not a grouping name",
@@ -1947,9 +2023,14 @@ compilePassOpcode(FileInfo *nested, TranslationTableOpcode opcode,
 				rule = (TranslationTableRule *)&(*table)->ruleArea[ruleOffset];
 			if (rule && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd ||
 								rule->opcode == CTO_SwapDd)) {
-				passInstructions[passIC++] = pass_swap;
-				passInstructions[passIC++] = ruleOffset >> 16;
-				passInstructions[passIC++] = ruleOffset & 0xffff;
+				if (!appendInstructionChar(passNested, passInstructions, &passIC, pass_swap))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset >> 16))
+					return 0;
+				if (!appendInstructionChar(
+							passNested, passInstructions, &passIC, ruleOffset & 0xffff))
+					return 0;
 				break;
 			}
 			compileError(passNested, "%s is not a swap name.",
-- 
2.33.0

