From 527deee86ba704b3bd99de3fa5bc127a9def78e7 Mon Sep 17 00:00:00 2001 From: Mate Soos Date: Mon, 22 Jun 2026 15:05:03 +0200 Subject: [PATCH 1/4] Validate Unicode direction override markers in documentation comments Update changelog More const Less const Less const Update const --- Changelog.md | 1 + liblangutil/Scanner.cpp | 15 ++++++++++++--- ...multiline_doc_unicode_direction_override_1.sol | 9 +++++++++ ...multiline_doc_unicode_direction_override_2.sol | 9 +++++++++ ...multiline_doc_unicode_direction_override_3.sol | 8 ++++++++ ...ingleline_doc_unicode_direction_override_1.sol | 9 +++++++++ ...ingleline_doc_unicode_direction_override_2.sol | 9 +++++++++ ...ingleline_doc_unicode_direction_override_3.sol | 8 ++++++++ 8 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_1.sol create mode 100644 test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_2.sol create mode 100644 test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_3.sol create mode 100644 test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_1.sol create mode 100644 test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_2.sol create mode 100644 test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_3.sol diff --git a/Changelog.md b/Changelog.md index 441ff22dcc89..d4df97989d34 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: * NatSpec: Disallow `@return` tag in event documentation. +* Parser: Detect unbalanced Unicode direction override markers (e.g. `U+202E`) in documentation comments (`///` and `/** */`) * SMTChecker: Fix incorrect handling of constant operands of unary operations. diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 4a41cb97a5f6..2391b5012d10 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -348,6 +348,7 @@ bool Scanner::tryScanEndOfLine() size_t Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); + size_t const startPosition = m_source.position(); size_t endPosition = m_source.position(); skipWhitespaceExceptUnicodeLinebreak(); @@ -385,12 +386,15 @@ size_t Scanner::scanSingleLineDocComment() advance(); } literal.complete(); + ScannerError const unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); + if (unicodeDirectionError != ScannerError::NoError) + m_skippedComments[NextNext].error = unicodeDirectionError; return endPosition; } Token Scanner::skipMultiLineComment() { - size_t startPosition = m_source.position(); + size_t const startPosition = m_source.position(); while (!isSourcePastEndOfInput()) { char prevChar = m_char; @@ -416,6 +420,7 @@ Token Scanner::skipMultiLineComment() Token Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); + size_t const startPosition = m_source.position(); bool endFound = false; bool charsAdded = false; @@ -464,8 +469,10 @@ Token Scanner::scanMultiLineDocComment() literal.complete(); if (!endFound) return setError(ScannerError::IllegalCommentTerminator); - else - return Token::CommentLiteral; + ScannerError const unicodeDirectionError = validateBiDiMarkup(m_source, startPosition); + if (unicodeDirectionError != ScannerError::NoError) + return setError(unicodeDirectionError); + return Token::CommentLiteral; } Token Scanner::scanSlash() @@ -488,6 +495,8 @@ Token Scanner::scanSlash() m_skippedComments[NextNext].location.sourceName = m_sourceName; m_skippedComments[NextNext].token = Token::CommentLiteral; m_skippedComments[NextNext].location.end = static_cast(scanSingleLineDocComment()); + if (m_skippedComments[NextNext].error != ScannerError::NoError) + return setError(m_skippedComments[NextNext].error); return Token::Whitespace; } else diff --git a/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_1.sol b/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_1.sol new file mode 100644 index 000000000000..1d94486b9e0c --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_1.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO + /** overflow ‮ */ + } +} +// ---- +// ParserError 8936: (71-90): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_2.sol b/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_2.sol new file mode 100644 index 000000000000..60389f9b778b --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_2.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF + /** underflow ‬ */ + } +} +// ---- +// ParserError 8936: (71-85): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_3.sol b/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_3.sol new file mode 100644 index 000000000000..e5c48ab73750 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_doc_unicode_direction_override_3.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure + { + // RLO PDF + /** ok ‮‬ */ + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_1.sol b/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_1.sol new file mode 100644 index 000000000000..699f52942c6c --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_1.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO + /// overflow ‮ + } +} +// ---- +// ParserError 8936: (71-92): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_2.sol b/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_2.sol new file mode 100644 index 000000000000..a33f944c8274 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_2.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF + /// underflow ‬ + } +} +// ---- +// ParserError 8936: (71-85): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_3.sol b/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_3.sol new file mode 100644 index 000000000000..1423f2928671 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_doc_unicode_direction_override_3.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure + { + // RLO PDF + /// ok ‮‬ + } +} +// ---- From a6b6225cffb3c3d37326383969b8195b6aca220c Mon Sep 17 00:00:00 2001 From: Mate Soos Date: Mon, 6 Jul 2026 10:47:36 +0200 Subject: [PATCH 2/4] Less noise --- liblangutil/Scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 2391b5012d10..d937c8197942 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -394,7 +394,7 @@ size_t Scanner::scanSingleLineDocComment() Token Scanner::skipMultiLineComment() { - size_t const startPosition = m_source.position(); + size_t startPosition = m_source.position(); while (!isSourcePastEndOfInput()) { char prevChar = m_char; From 5072ba443c8a6c501178c6a4fd4a147574b4cb93 Mon Sep 17 00:00:00 2001 From: Mate Soos Date: Mon, 6 Jul 2026 10:48:05 +0200 Subject: [PATCH 3/4] Less noise --- liblangutil/Scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index d937c8197942..4b18af3879af 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -420,7 +420,7 @@ Token Scanner::skipMultiLineComment() Token Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); - size_t const startPosition = m_source.position(); + size_t startPosition = m_source.position(); bool endFound = false; bool charsAdded = false; From 5a9fb8c58d272fe54a1087549c537606a0852093 Mon Sep 17 00:00:00 2001 From: Mate Soos Date: Mon, 6 Jul 2026 10:48:29 +0200 Subject: [PATCH 4/4] Less noise --- liblangutil/Scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 4b18af3879af..d937c8197942 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -420,7 +420,7 @@ Token Scanner::skipMultiLineComment() Token Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); - size_t startPosition = m_source.position(); + size_t const startPosition = m_source.position(); bool endFound = false; bool charsAdded = false;