From 52567de8c5db0b95abb1372194d652b1186cbef2 Mon Sep 17 00:00:00 2001
From: Alex Chekunkov <86681657+alexc-db@users.noreply.github.com>
Date: Fri, 29 Oct 2021 01:45:06 -0700
Subject: [PATCH] Add checker for static final buffers (#12)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add validation for static final byte buffers to make sure they are unreleasable and read-only as a follow up for https://github.com/netty/netty/issues/11792. Also enabled support for checkstyle warning suppression using `SuppressWarnings` annotation.
Verified by installing it locally and running it against local Netty code. Example:
```
→ mvn validate
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Detecting the operating system and CPU architecture
[INFO] ------------------------------------------------------------------------
[INFO] os.detected.name: osx
[INFO] os.detected.arch: x86_64
[INFO] os.detected.bitness: 64
[INFO] os.detected.version: 10.16
[INFO] os.detected.version.major: 10
[INFO] os.detected.version.minor: 16
[INFO] os.detected.classifier: osx-x86_64
[INFO]
[INFO] ---------------------< io.netty:netty-codec-http >----------------------
[INFO] Building Netty/Codec/HTTP 4.1.70.Final-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-enforcer-plugin:1.4.1:enforce (enforce-maven) @ netty-codec-http ---
[INFO]
[INFO] --- maven-enforcer-plugin:1.4.1:enforce (enforce-tools) @ netty-codec-http ---
[INFO]
[INFO] --- maven-checkstyle-plugin:3.1.0:check (check-style) @ netty-codec-http ---
[INFO] Starting audit...
[ERROR] /Users/alex.chekunkov/Dev/github.com/alexc-db/netty/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java:53: static final buffer assignment should match pattern (Unpooled\s*\.)?unreleasableBuffer\(.*?\)\s*\.asReadOnly\(\) [StaticFinalBuffer]
[ERROR] /Users/alex.chekunkov/Dev/github.com/alexc-db/netty/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java:54: static final buffer assignment should match pattern (Unpooled\s*\.)?unreleasableBuffer\(.*?\)\s*\.asReadOnly\(\) [StaticFinalBuffer]
[ERROR] /Users/alex.chekunkov/Dev/github.com/alexc-db/netty/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.java:34: static final buffer assignment should match pattern (Unpooled\s*\.)?unreleasableBuffer\(.*?\)\s*\.asReadOnly\(\) [StaticFinalBuffer]
[ERROR] /Users/alex.chekunkov/Dev/github.com/alexc-db/netty/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.java:36: static final buffer assignment should match pattern (Unpooled\s*\.)?unreleasableBuffer\(.*?\)\s*\.asReadOnly\(\) [StaticFinalBuffer]
[ERROR] /Users/alex.chekunkov/Dev/github.com/alexc-db/netty/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.java:38: static final buffer assignment should match pattern (Unpooled\s*\.)?unreleasableBuffer\(.*?\)\s*\.asReadOnly\(\) [StaticFinalBuffer]
Audit done.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.755 s
[INFO] Finished at: 2021-10-26T20:00:56-07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.1.0:check (check-style) on project netty-codec-http: Failed during checkstyle execution: There are 5 errors reported by Checkstyle 8.29 with io/netty/checkstyle.xml ruleset. -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
```
Findings from the check are fixed in https://github.com/netty/netty/pull/11802.
---
.../checkstyle/StaticFinalBufferCheck.java | 79 +++++++++++++++++++
.../main/resources/io/netty/checkstyle.xml | 6 +-
2 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 common/src/main/java/io/netty/build/checkstyle/StaticFinalBufferCheck.java
diff --git a/common/src/main/java/io/netty/build/checkstyle/StaticFinalBufferCheck.java b/common/src/main/java/io/netty/build/checkstyle/StaticFinalBufferCheck.java
new file mode 100644
index 0000000..839983f
--- /dev/null
+++ b/common/src/main/java/io/netty/build/checkstyle/StaticFinalBufferCheck.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.netty.build.checkstyle;
+
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.FileContents;
+import com.puppycrawl.tools.checkstyle.api.FullIdent;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+
+import java.util.regex.Pattern;
+
+/**
+ * This check verifies that static final buffers are unreleasable and read-only.
+ *
+ * It aims to prevent corruption bugs like https://github.com/netty/netty/issues/11792
+ * from happening in the future.
+ */
+public class StaticFinalBufferCheck extends AbstractCheck {
+
+ // Pattern is not multiline because variable definition is flattened and trimmed before the match.
+ private static final Pattern pattern = Pattern.compile(
+ "(Unpooled\\s*\\.)?unreleasableBuffer\\(.*?\\)\\s*\\.asReadOnly\\(\\)");
+
+ @Override
+ public int[] getRequiredTokens() {
+ return new int[]{TokenTypes.VARIABLE_DEF};
+ }
+
+ @Override
+ public int[] getDefaultTokens() {
+ return getRequiredTokens();
+ }
+
+ @Override
+ public int[] getAcceptableTokens() {
+ return getRequiredTokens();
+ }
+
+ @Override
+ public void visitToken(DetailAST ast) {
+ DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
+ boolean isStatic = modifiersAST.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
+ boolean isFinal = modifiersAST.findFirstToken(TokenTypes.FINAL) != null;
+ FullIdent typeIdent = FullIdent.createFullIdentBelow(ast.findFirstToken(TokenTypes.TYPE));
+ if (!isStatic || !isFinal || !typeIdent.getText().endsWith("Buf")) {
+ return;
+ }
+ DetailAST assignAST = ast.findFirstToken(TokenTypes.ASSIGN);
+ DetailAST semiAST = ast.findFirstToken(TokenTypes.SEMI);
+ if (assignAST == null || semiAST == null) {
+ log(ast.getLineNo(), "Missing assignment for static final buffer");
+ return;
+ }
+ FileContents fc = getFileContents();
+ StringBuilder sb = new StringBuilder();
+ for (int i = assignAST.getLineNo(); i <= semiAST.getLineNo(); i++) {
+ // getLineNo returns 1-based line number, getLine expects 0-based.
+ sb.append(fc.getLine(i - 1).trim());
+ }
+ if (!pattern.matcher(sb.toString()).find()) {
+ log(ast.getLineNo(), "static final buffer assignment should match pattern " + pattern);
+ }
+ }
+}
diff --git a/common/src/main/resources/io/netty/checkstyle.xml b/common/src/main/resources/io/netty/checkstyle.xml
index aa40d65..77fd829 100644
--- a/common/src/main/resources/io/netty/checkstyle.xml
+++ b/common/src/main/resources/io/netty/checkstyle.xml
@@ -55,7 +55,7 @@
-
+
@@ -75,8 +75,12 @@
+
+
+
+