Skip to content

Commit

Permalink
Validate root-level devictree entries in the keymap devicetree
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chrisandreae committed Nov 19, 2023
1 parent b96441a commit 8759c95
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 3 deletions.
35 changes: 34 additions & 1 deletion lambda/compiler.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
15 changes: 13 additions & 2 deletions release.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"
'';
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 8759c95

Please sign in to comment.