Received: by 2002:a25:8b12:0:0:0:0:0 with SMTP id i18csp2749178ybl; Thu, 29 Aug 2019 12:19:15 -0700 (PDT) X-Google-Smtp-Source: APXvYqxuuyXwKrkavn65HAKHgkb1xOPMVI4sVt7NYw83KnZaGAgv9Vf36fLnw5r0E+dd+xHJCmsU X-Received: by 2002:a63:4042:: with SMTP id n63mr9504113pga.75.1567106355673; Thu, 29 Aug 2019 12:19:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1567106355; cv=none; d=google.com; s=arc-20160816; b=NZ2ozONFFTv7AXm3Ykk1L0MLiW98qvAHY/3Tca3MQ6NWRbhywxOzDjYowrP1zMcLu3 jAEu92V4ZDUi4Z9YiR0jw7N9x2dwrwLYd6zAss9w25Y5ra7zIl1NyV9MFrP9qbrc5WZf sm/O/F9fYC+o/kydbXgt4KGEM+hyeevFm2FGz2utPDilF+Hf1aCUQUOPzWItrJ+XJFOz zDva1bDT2jXMCrQ46NkYfjHogUmL4mf85B+TOCTJ07Sjw5Iay5mdLkv1FiVN9bwFeE7w 8peB5fShs7pIGGULrdCoKRG1FiRprRjksO9jP4c0V4KIBwNcyWK7oQvWcgh59UHCUPnd ZOJg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:in-reply-to:content-disposition :mime-version:references:message-id:subject:cc:to:from:date :dkim-signature; bh=h82RDi7ZzNpfF4AJAy2P12pYrYk0GOcUf/rAKW7b39I=; b=zh1XOTHyPCWJ+GUkG9Ea7s3kVSTZdzlOgeWRZbpaHgPsqkCJC7QdfENMxkMHSfYr/m 14eIRQDDeTz7lW41qIkmQroRyrKJWX4sovpsGUJKcUfqgQWZVuVtHN3IapebjTloZw1l /eZCa5a13tv0igezd+16X2llwCNnvqNQAViw7dInBA7c5tgxbJOzNJVyOkqOtN8/F5VS zcU6APmHx39kV2KHaHZACmtB64ejXE8Zr0H8/L782LioHHbFuokSyCt4d/CLAWDQMX+g g2UtEru0/WR2avd12fvMJwYkfPfWi/LI+aZl+SfxRI6rT0Uu8BgCvGUDaSMlvX+qDkYT eogg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=Fewo6YSb; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q128si3583811pfb.192.2019.08.29.12.19.00; Thu, 29 Aug 2019 12:19:15 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=Fewo6YSb; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728870AbfH2TSC (ORCPT + 99 others); Thu, 29 Aug 2019 15:18:02 -0400 Received: from mail-pl1-f194.google.com ([209.85.214.194]:41570 "EHLO mail-pl1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728538AbfH2TSC (ORCPT ); Thu, 29 Aug 2019 15:18:02 -0400 Received: by mail-pl1-f194.google.com with SMTP id m9so2025444pls.8 for ; Thu, 29 Aug 2019 12:18:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=h82RDi7ZzNpfF4AJAy2P12pYrYk0GOcUf/rAKW7b39I=; b=Fewo6YSb0G8pbm3NVKWjPJJ4aQFAxcAV3z7nvn0LVcFYaw7bkl+6v7OtUiPmt6wUfM 2EM/xtMH3lZd7ShILVPWS5BUvWb0zp4FAMGMSxRS7/Ew0BcT8FGXbJrxhNRlsu5Oq4tn diDwd55+pIbAB+cxaj8HItHF91ZVzOxlfA2sE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=h82RDi7ZzNpfF4AJAy2P12pYrYk0GOcUf/rAKW7b39I=; b=JFbacQgtnYxBx5FhMP+6E/pF4ZcgKpF7HDizYvujRxfKEBZICheY1WCyiz/J7EHPSp zqvtwSfKRQqgaDSvuLZrqjX5gLwgCFNLIDrdCyk7uzROhYy34vOgf0Da3eVoPxye4EdR qWSAFTGMCZcNmBak9AaWvq128S9AvR3XEZkRSlGkWQ1qKwou1GjQm7qwHycLgRNIPrX/ TN6ERsYcWZ1ftJaIvzcCd8jIn1TBa20WskQRGW3TlFpxPhLFpOwZvLGFHSkPrkr9QMkB NHVayDcP5CgQJQFjlPZIYX0MpCRFIx6TRIC1g6L3ugYBqyGML7pVfJ/4niUAu0CpHuuv z+ag== X-Gm-Message-State: APjAAAVwp1JgLu8uZXsGcJnM9QQYpiN2W5lQC3ZElaNygc74IqsHfEgB CcPPoNw3mc3hkd2mYNKX/orIRBIfcP4= X-Received: by 2002:a17:902:9889:: with SMTP id s9mr11870896plp.100.1567106281373; Thu, 29 Aug 2019 12:18:01 -0700 (PDT) Received: from www.outflux.net (smtp.outflux.net. [198.145.64.163]) by smtp.gmail.com with ESMTPSA id c35sm2864072pgl.72.2019.08.29.12.18.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Aug 2019 12:18:00 -0700 (PDT) Date: Thu, 29 Aug 2019 12:17:59 -0700 From: Kees Cook To: Nathan Huckleberry Cc: mark.rutland@arm.com, clang-built-linux@googlegroups.com, linux-kernel@vger.kernel.org, Masahiro Yamada , linux-kbuild@vger.kernel.org Subject: Re: [RFC PATCH 1/2] Add clang-tidy and static analyzer support to makefile Message-ID: <201908291207.3AE0872B88@keescook> References: <20190806211047.232709-1-nhuck@google.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20190806211047.232709-1-nhuck@google.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org [ Added kbuild and Masahiro to CC ... ] On Tue, Aug 06, 2019 at 02:10:46PM -0700, Nathan Huckleberry wrote: > Signed-off-by: Nathan Huckleberry > --- These two lines should be at the end of your commit log. :) > These patches add clang-tidy and the clang static-analyzer as make > targets. The goal of these patches is to make static analysis tools > usable and extendable by any developer or researcher who is familiar > with basic c++. > > The current static analysis tools require intimate knowledge of the internal > workings of the static analysis. Clang-tidy and the clang static analyzers > expose an easy to use api and allow users unfamiliar with clang to > write new checks with relative ease. > > ===Clang-tidy=== > > Clang-tidy is an easily extendable 'linter' that runs on the AST. > Clang-tidy checks are easy to write and understand. A check consists of > two parts, a matcher and a checker. The matcher is created using a > domain specific language that acts on the AST > (https://clang.llvm.org/docs/LibASTMatchersReference.html). When AST > nodes are found by the matcher a callback is made to the checker. The > checker can then execute additional checks and issue warnings. > > Here is an example clang-tidy check to report functions that have calls > to local_irq_disable without calls to local_irq_enable and vice-versa. > Functions flagged with __attribute((annotation("ignore_irq_balancing"))) > are ignored for analysis. > > The full patch can be found here (https://reviews.llvm.org/D65828) > > ``` > void IrqUnbalancedCheck::registerMatchers(MatchFinder *Finder) { > // finds calls to "arch_local_irq_disable" in a function body > auto disable = > forEachDescendant( > callExpr( > hasDeclaration( > namedDecl( > hasName("arch_local_irq_disable")))).bind("disable")); > > // finds calls to "arch_local_irq_enable" in a function body > auto enable = > forEachDescendant( > callExpr( > hasDeclaration( > namedDecl( > hasName("arch_local_irq_enable")))).bind("enable")); > > // Looks for function body that has the following property: > // ((disable && !enable) || (enable && !disable)) > auto matcher = functionDecl( > anyOf(allOf(disable, unless(enable)), allOf(enable, unless(disable)))); > > Finder->addMatcher(matcher.bind("func"), this); > } > > std::string annotation = "ignore_irq_balancing"; > void IrqUnbalancedCheck::check(const MatchFinder::MatchResult &Result) { > const auto *MatchedDecl = Result.Nodes.getNodeAs("func"); > const auto *DisableCall = Result.Nodes.getNodeAs("disable"); > const auto *EnableCall = Result.Nodes.getNodeAs("enable"); > > // If the function has __attribute((annotate("ignore_irq_balancing")) > for (const auto *Attr : MatchedDecl->attrs()) { > if (Attr->getKind() == clang::attr::Annotate) { > if(dyn_cast(Attr)->getAnnotation().str() == annotation) { > return; > } > } > } > > if(EnableCall) { > diag(MatchedDecl->getLocation(), "call to 'enable_local_irq' without 'disable_local_irq' in %0 ") > << MatchedDecl; > diag(EnableCall->getBeginLoc(), "call to 'enable_local_irq'", DiagnosticIDs::Note) > << MatchedDecl; > } > > if(DisableCall) { > diag(MatchedDecl->getLocation(), "call to 'disable_local_irq' without 'enable_local_irq' in %0 ") > << MatchedDecl; > diag(DisableCall->getBeginLoc(), "call to 'disable_local_irq'", DiagnosticIDs::Note) > << MatchedDecl; > } > } > ``` > > ===Clang static analyzer=== > > The clang static analyzer is a more powerful static analysis tool that > uses symbolic execution to find bugs. Currently there is a check that > looks for potential security bugs from invalid uses of kmalloc and > kfree. There are several more general purpose checks that are useful for > the kernel. > > The clang static analyzer is well documented and designed to be > extensible. > (https://clang-analyzer.llvm.org/checker_dev_manual.html) > (https://github.com/haoNoQ/clang-analyzer-guide/releases/download/v0.1/clang-analyzer-guide-v0.1.pdf) > > > Why add clang-tidy and the clang static analyzer when other static > analyzers are already in the kernel? > > The main draw of the clang tools is how accessible they are. The clang > documentation is very nice and these tools are built specifically to be > easily extendable by any developer. They provide an accessible method of > bug-finding and research to people who are not overly familiar with the > kernel codebase. > ^ i.e. they should be right here... > Makefile | 3 ++ > scripts/clang-tools/Makefile.clang-tools | 35 ++++++++++++++ > .../{ => clang-tools}/gen_compile_commands.py | 0 > scripts/clang-tools/parse_compile_commands.py | 47 +++++++++++++++++++ > 4 files changed, 85 insertions(+) > create mode 100644 scripts/clang-tools/Makefile.clang-tools > rename scripts/{ => clang-tools}/gen_compile_commands.py (100%) > create mode 100755 scripts/clang-tools/parse_compile_commands.py > > diff --git a/Makefile b/Makefile > index fabc127d127f..49f1d3fa48a8 100644 > --- a/Makefile > +++ b/Makefile > @@ -709,6 +709,7 @@ KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0) > > include scripts/Makefile.kcov > include scripts/Makefile.gcc-plugins > +include scripts/clang-tools/Makefile.clang-tools > > ifdef CONFIG_READABLE_ASM > # Disable optimizations that make assembler listings hard to read. > @@ -1470,6 +1471,8 @@ help: > @echo ' headers_check - Sanity check on exported headers' > @echo ' headerdep - Detect inclusion cycles in headers' > @echo ' coccicheck - Check with Coccinelle' > + @echo ' clang-analyzer - Check with clang static analyzer' > + @echo ' clang-tidy - Check with clang-tidy' I think this is great; more people will be able to run these tests. > @echo '' > @echo 'Kernel selftest:' > @echo ' kselftest - Build and run kernel selftest (run as root)' > diff --git a/scripts/clang-tools/Makefile.clang-tools b/scripts/clang-tools/Makefile.clang-tools > new file mode 100644 > index 000000000000..0adb6df20777 > --- /dev/null > +++ b/scripts/clang-tools/Makefile.clang-tools > @@ -0,0 +1,35 @@ > +# SPDX-License-Identifier: GPL-2.0 > +PHONY += clang-tidy > +HAS_PARALLEL := $(shell (parallel --version 2> /dev/null) | grep 'GNU parallel' 2> /dev/null) > +clang-tidy: > +ifdef CONFIG_CC_IS_CLANG > + $(PYTHON3) scripts/clang-tools/gen_compile_commands.py > +ifdef HAS_PARALLEL Is it worth falling back to xargs? Are there builders where clang-tidy or clang-analyzer are installed but parallel isn't? (i.e. it might just be better to simply require parallel.) Note that there's no test for python3 -- we just try to run it. :) > + #Xargs interleaves multiprocessed output. GNU Parallel does not. > + scripts/clang-tools/parse_compile_commands.py compile_commands.json \ > + | parallel -k -j $(shell nproc) 'echo {} && clang-tidy -p . "-checks=-*,linuxkernel-*" {}' > +else > + @echo "GNU parallel is not installed. Defaulting to non-parallelized runs" > + scripts/clang-tools/parse_compile_commands.py compile_commands.json \ > + | xargs -L 1 -I@ sh -c "echo '@' && clang-tidy -p . '-checks=-*,linuxkernel-*' @" > +endif > +else > + $(error Clang-tidy requires CC=clang) > +endif > + > +PHONY += clang-analyzer > +clang-analyzer: > +ifdef CONFIG_CC_IS_CLANG > + $(PYTHON3) scripts/clang-tools/gen_compile_commands.py > +ifdef HAS_PARALLEL > + #Xargs interleaves multiprocessed output. GNU Parallel does not. > + scripts/clang-tools/parse_compile_commands.py compile_commands.json \ > + | parallel -k -j $(shell nproc) 'echo {} && clang-tidy -p . "-checks=-*,clang-analyzer-*" {}' > +else > + @echo "GNU parallel is not installed. Defaulting to non-parallelized runs" > + scripts/clang-tools/parse_compile_commands.py compile_commands.json \ > + | xargs -L 1 -I@ sh -c "echo '@' && clang-tidy -p . '-checks=-*,clang-analyzer-*' @" > +endif > +else > + $(error Clang-analyzer requires CC=clang) > +endif > diff --git a/scripts/gen_compile_commands.py b/scripts/clang-tools/gen_compile_commands.py > similarity index 100% > rename from scripts/gen_compile_commands.py > rename to scripts/clang-tools/gen_compile_commands.py > diff --git a/scripts/clang-tools/parse_compile_commands.py b/scripts/clang-tools/parse_compile_commands.py > new file mode 100755 > index 000000000000..d6bc1bf9951e > --- /dev/null > +++ b/scripts/clang-tools/parse_compile_commands.py > @@ -0,0 +1,47 @@ > +#!/usr/bin/env python > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) Google LLC, 2019 > +# > +# Author: Nathan Huckleberry > +# > +"""A helper routine for make clang-tidy to parse compile_commands.json.""" > + > +import argparse > +import json > +import logging > +import os > +import re > + > +def parse_arguments(): > + """Set up and parses command-line arguments. > + Returns: > + file_path: Path to compile_commands.json file > + """ > + usage = """Parse a compilation database and return a list of files > + included in the database""" > + parser = argparse.ArgumentParser(description=usage) > + > + file_path_help = ('Path to the compilation database to parse') > + parser.add_argument('file', type=str, help=file_path_help) > + > + args = parser.parse_args() > + > + return args.file > + > + > +def main(): > + filename = parse_arguments() > + > + #Read JSON data into the datastore variable > + if filename: > + with open(filename, 'r') as f: > + datastore = json.load(f) > + > + #Use the new datastore datastructure > + for entry in datastore: > + print(entry['file']) > + > + > +if __name__ == '__main__': > + main() And this is nice to have because now there is a real consumer of gen_compile_commands.py. -- Kees Cook