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 @@ + + + +