From 8759c950dcc87360c4fd1d92990c13f57d1d386f Mon Sep 17 00:00:00 2001 From: Chris Andreae Date: Thu, 16 Nov 2023 19:37:35 +0900 Subject: [PATCH] Validate root-level devictree entries in the keymap devicetree In order to prevent unsafe devicetree overriding via keymap files, require the root level devicetree entries in the provided keymap to match a strict allow-list. This is in fact more restrictive than necessary, because it'd be legitimate to define for example a `compatible = "zmk,behavior-hold-tap";` section at the top level, but it's much easier to safely validate. --- lambda/compiler.rb | 35 ++++++++++++++++++++++++++++++++++- release.nix | 15 +++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/lambda/compiler.rb b/lambda/compiler.rb index 1dba233afac..9e9b8848552 100644 --- a/lambda/compiler.rb +++ b/lambda/compiler.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true require 'tmpdir' -require 'json' require 'base64' +require 'json' +require 'open3' +require 'yaml' class Compiler class CompileError < RuntimeError @@ -33,6 +35,7 @@ def compile_board(board, keymap_data:, kconfig_data:, include_static_rhs: false) compile_command = ['compileZmk', '-b', board] if keymap_data + validate_devicetree!(keymap_data) File.open('build.keymap', 'w') { |io| io.write(keymap_data) } compile_command << '-k' << './build.keymap' end @@ -70,6 +73,36 @@ def compile_board(board, keymap_data:, kconfig_data:, include_static_rhs: false) end end + PERMITTED_DTS_SECTIONS = %w[ + behaviors macros combos conditional_layers keymap underglow-indicators + ].freeze + + def validate_devicetree!(dtsi) + dts = "/dts-v1/;\n" + dtsi + + stdout, stderr, status = + Open3.capture3({}, 'dts2yml', unsetenv_others: true, stdin_data: dts) + + unless status.success? + raise CompileError.new('Syntax error checking device-tree input', log: stderr.split("\n")) + end + + data = + begin + YAML.safe_load(stdout) + rescue Psych::Exception => e + raise CompileError.new('Error parsing translated device-tree', status: 500, log: [e.message]) + end + + sections = data.flat_map(&:keys) + invalid_sections = sections - PERMITTED_DTS_SECTIONS + + unless invalid_sections.empty? + raise CompileError.new( + "Device-tree included the non-permitted root sections: #{invalid_sections.inspect}", log: []) + end + end + # Lambda is single-process per container, and we get substantial speedups # from ccache by always building in the same path BUILD_DIR = '/tmp/build' diff --git a/release.nix b/release.nix index 3ed1cb5ef8d..7366f6ed1e1 100644 --- a/release.nix +++ b/release.nix @@ -44,6 +44,17 @@ let includes = zmk.buildInputs ++ zmk.nativeBuildInputs ++ zmk.zephyrModuleDeps; }; + dts2yml = pkgs.writeShellScriptBin "dts2yml" '' + set -eo pipefail + + ${pkgs.gcc-arm-embedded}/bin/arm-none-eabi-cpp -P -D__DTS__ -E -nostdinc \ + -I "${zmk.src}/app/dts" -I "${zmk.src}/app/include" \ + -I "${zephyr}/zephyr/dts" -I "${zephyr}/zephyr/dts/common" -I "${zephyr}/zephyr/dts/arm" \ + -I "${zephyr}/zephyr/include" -I "${zephyr}/zephyr/include/zephyr"\ + -undef -x assembler-with-cpp - |\ + ${pkgs.dtc}/bin/dtc -I dts -O yaml + ''; + zmkCompileScript = let zmk' = zmk.override { gcc-arm-embedded = ccacheWrapper.override { @@ -161,7 +172,7 @@ let startLambda = pkgs.writeShellScriptBin "startLambda" '' set -euo pipefail - export PATH=${lib.makeBinPath [ zmkCompileScript ]}:$PATH + export PATH=${lib.makeBinPath [ zmkCompileScript dts2yml ]}:$PATH cd ${lambda.source} ${lambda.bundleEnv}/bin/bundle exec aws_lambda_ric "app.LambdaFunction::Handler.process" ''; @@ -189,7 +200,7 @@ let }; }; in { - inherit lambdaImage zmkCompileScript ccacheCache; + inherit lambdaImage zmkCompileScript dts2yml ccacheCache; directLambdaImage = lambdaImage; # nix shell -f release.nix simulateLambda -c simulateLambda