kconfig-hardened-check.git
20 months agoFix pylint warning: formatting a regular string which could be a f-string (II)
Alexander Popov [Sun, 26 Feb 2023 06:31:05 +0000 (09:31 +0300)]
Fix pylint warning: formatting a regular string which could be a f-string (II)

Part II, assert()

20 months agoFix pylint warning: formatting a regular string which could be a f-string (I)
Alexander Popov [Sun, 26 Feb 2023 05:58:45 +0000 (08:58 +0300)]
Fix pylint warning: formatting a regular string which could be a f-string (I)

Part I, print()

20 months agoImprove the 'mitigations' check
Alexander Popov [Fri, 17 Feb 2023 17:12:52 +0000 (20:12 +0300)]
Improve the 'mitigations' check

Thanks to @izh1979

20 months agoAdd more info about perf_event_paranoid
Alexander Popov [Thu, 16 Feb 2023 16:11:38 +0000 (19:11 +0300)]
Add more info about perf_event_paranoid

Thanks to @izh1979

21 months agoReturn the AIO check
Alexander Popov [Sat, 4 Feb 2023 14:04:40 +0000 (17:04 +0300)]
Return the AIO check

Linus about AIO: https://lwn.net/Articles/671657/
LWN: Fixing asynchronous I/O, again: https://lwn.net/Articles/671649/

21 months agoAdd the NOUVEAU_LEGACY_CTX_SUPPORT check
Alexander Popov [Sat, 4 Feb 2023 13:22:42 +0000 (16:22 +0300)]
Add the NOUVEAU_LEGACY_CTX_SUPPORT check

See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b30a43ac7132cdda833ac4b13dd1ebd35ace14b7

Dave Airlie:
There was a nouveau DDX that relied on legacy context ioctls to work,
but we fixed it years ago, give distros that have a modern DDX the
option to break the uAPI and close the mess of holes that legacy
context support is.

21 months agoDon't use the uninitialized 'parsed_cmdline_options' variable
Alexander Popov [Sat, 4 Feb 2023 11:57:54 +0000 (14:57 +0300)]
Don't use the uninitialized 'parsed_cmdline_options' variable

21 months agoUpdate the README
Alexander Popov [Sun, 22 Jan 2023 00:37:51 +0000 (03:37 +0300)]
Update the README

Ready for release 0.6.1.

21 months agoFix the 'decision' for the slub_merge check
Alexander Popov [Sun, 22 Jan 2023 22:16:52 +0000 (01:16 +0300)]
Fix the 'decision' for the slub_merge check

21 months agoFix the pylint R1714 issues
Alexander Popov [Sun, 22 Jan 2023 00:16:01 +0000 (03:16 +0300)]
Fix the pylint R1714 issues

No functional changes

21 months agoAdd the check for the 'kvm.nx_huge_pages' cmdline parameter
Alexander Popov [Sat, 21 Jan 2023 22:52:33 +0000 (01:52 +0300)]
Add the check for the 'kvm.nx_huge_pages' cmdline parameter

21 months agoAdd the check for the slub_merge cmdline parameter
Alexander Popov [Sat, 21 Jan 2023 22:41:43 +0000 (01:41 +0300)]
Add the check for the slub_merge cmdline parameter

21 months agoRename the file with the KSPP cmdline recommendations to avoid breaking the CI
Alexander Popov [Sat, 21 Jan 2023 22:07:42 +0000 (01:07 +0300)]
Rename the file with the KSPP cmdline recommendations to avoid breaking the CI

21 months agoClean up the comments
Alexander Popov [Sat, 21 Jan 2023 22:00:31 +0000 (01:00 +0300)]
Clean up the comments

21 months agoAdd the check for the iommu cmdline option
Alexander Popov [Sat, 21 Jan 2023 21:58:41 +0000 (00:58 +0300)]
Add the check for the iommu cmdline option

Refers to #76

21 months agoAdd the KSPP cmdline recommendations for x86_64
Alexander Popov [Sat, 21 Jan 2023 12:30:56 +0000 (15:30 +0300)]
Add the KSPP cmdline recommendations for x86_64

21 months agoInclude parsed_cmdline_options in the print_unknown_options() call
Alexander Popov [Sat, 21 Jan 2023 12:23:13 +0000 (15:23 +0300)]
Include parsed_cmdline_options in the print_unknown_options() call

21 months agoRename the files with the KSPP recommendations
Alexander Popov [Sat, 21 Jan 2023 10:53:39 +0000 (13:53 +0300)]
Rename the files with the KSPP recommendations

21 months agoDrop get-nix-kconfig.py (`nix-build get-nixos-kconfig.nix` does the job)
Alexander Popov [Thu, 19 Jan 2023 15:54:49 +0000 (18:54 +0300)]
Drop get-nix-kconfig.py (`nix-build get-nixos-kconfig.nix` does the job)

Refers to #77 and #63.

21 months agoAdd the NixOS kernel configs
Alexander Popov [Thu, 19 Jan 2023 15:34:12 +0000 (18:34 +0300)]
Add the NixOS kernel configs

Refers to #77

21 months agoMerge branch 'from-o8opi-2'
Alexander Popov [Thu, 19 Jan 2023 15:10:54 +0000 (18:10 +0300)]
Merge branch 'from-o8opi-2'

Thanks, @o8opi

21 months agoUpdate the VMAP_STACK check: it is available for ARM
Alexander Popov [Sun, 15 Jan 2023 21:10:12 +0000 (00:10 +0300)]
Update the VMAP_STACK check: it is available for ARM

21 months agoFix the arch condition for the SCHED_CORE check (II)
Alexander Popov [Sun, 15 Jan 2023 20:06:20 +0000 (23:06 +0300)]
Fix the arch condition for the SCHED_CORE check (II)

21 months agoUpdate the KSPP recommendations
Alexander Popov [Sat, 14 Jan 2023 15:46:45 +0000 (18:46 +0300)]
Update the KSPP recommendations

21 months agoFix the IOMMU_DEFAULT_DMA_STRICT check: it is in defconfig for arm64 and arm
Alexander Popov [Sat, 14 Jan 2023 15:07:29 +0000 (18:07 +0300)]
Fix the IOMMU_DEFAULT_DMA_STRICT check: it is in defconfig for arm64 and arm

21 months agoAdd the COMPAT and X86_X32_ABI checks
Alexander Popov [Sat, 14 Jan 2023 10:47:21 +0000 (13:47 +0300)]
Add the COMPAT and X86_X32_ABI checks

KSPP has added them to the recommendations.

Refers to #74.

21 months agoFix the WERROR check: it is in defconfig for X86_64 and X86_32 now
Alexander Popov [Sat, 14 Jan 2023 10:39:49 +0000 (13:39 +0300)]
Fix the WERROR check: it is in defconfig for X86_64 and X86_32 now

21 months agoFix the DEBUG_WX check: it is in defconfig for X86_64 and X86_32 now
Alexander Popov [Sat, 14 Jan 2023 10:33:33 +0000 (13:33 +0300)]
Fix the DEBUG_WX check: it is in defconfig for X86_64 and X86_32 now

21 months agoAdd defconfigs for Linux v6.1
Alexander Popov [Fri, 13 Jan 2023 19:12:22 +0000 (22:12 +0300)]
Add defconfigs for Linux v6.1

22 months agoadd get-nixos-kconfig nix script 77/head
o8opi [Thu, 29 Dec 2022 09:02:02 +0000 (10:02 +0100)]
add get-nixos-kconfig nix script

22 months agoAdd the repository mirrors 64/head
Alexander Popov [Mon, 26 Dec 2022 09:58:06 +0000 (12:58 +0300)]
Add the repository mirrors

22 months agoBackup the pull requests and issues into a Markdown file using gh2md
Alexander Popov [Sun, 25 Dec 2022 18:03:04 +0000 (21:03 +0300)]
Backup the pull requests and issues into a Markdown file using gh2md

That would allow to have more information in the mirror repositories.

I used the mattduck/gh2md tool for that purpose.
It employs https://api.github.com/graphql, so I had to generate
a GitHub personal access token (classic) with public access.

22 months agoRemove the AIO check
Alexander Popov [Fri, 16 Dec 2022 23:35:43 +0000 (02:35 +0300)]
Remove the AIO check

Currently GrapheneOS doesn't have it.

23 months agoRemember about the nosmt sysfs control file
Alexander Popov [Sat, 10 Dec 2022 18:25:13 +0000 (21:25 +0300)]
Remember about the nosmt sysfs control file

23 months agoDrop the comment about mitigations of CPU vulnerabilities
Alexander Popov [Sat, 10 Dec 2022 18:24:42 +0000 (21:24 +0300)]
Drop the comment about mitigations of CPU vulnerabilities

The corresponding checks have been developed.

23 months agoSave the list of disabled mitigations of CPU vulnerabilities (for history)
Alexander Popov [Sat, 10 Dec 2022 18:18:34 +0000 (21:18 +0300)]
Save the list of disabled mitigations of CPU vulnerabilities (for history)

23 months agoAdd the nospectre_bhb check
Alexander Popov [Sat, 10 Dec 2022 17:44:23 +0000 (20:44 +0300)]
Add the nospectre_bhb check

23 months agoAdd the kpti check
Alexander Popov [Sat, 10 Dec 2022 08:01:16 +0000 (11:01 +0300)]
Add the kpti check

1. Don't add an exception to normalize_cmdline_options() since strtobool()
is used for kpti

2. Use new '0' check of 'is not off'

23 months agoCompare against '0' in the 'is not off' check
Alexander Popov [Sat, 10 Dec 2022 07:47:05 +0000 (10:47 +0300)]
Compare against '0' in the 'is not off' check

23 months agoAdd the tsx check
Alexander Popov [Sat, 10 Dec 2022 07:08:23 +0000 (10:08 +0300)]
Add the tsx check

23 months agoChange the 'decision' of X86_INTEL_TSX_MODE_OFF check to defconfig
Alexander Popov [Sat, 10 Dec 2022 07:07:10 +0000 (10:07 +0300)]
Change the 'decision' of X86_INTEL_TSX_MODE_OFF check to defconfig

23 months agoAdd the nomte check
Alexander Popov [Sat, 10 Dec 2022 06:33:43 +0000 (09:33 +0300)]
Add the nomte check

23 months agoAdd the nopauth check
Alexander Popov [Sat, 10 Dec 2022 06:32:51 +0000 (09:32 +0300)]
Add the nopauth check

23 months agoAdd the nobti check
Alexander Popov [Sat, 10 Dec 2022 06:32:21 +0000 (09:32 +0300)]
Add the nobti check

23 months agoAdd the sysrq_always_enabled check
Alexander Popov [Fri, 9 Dec 2022 18:00:08 +0000 (21:00 +0300)]
Add the sysrq_always_enabled check

23 months agoAdd the ssbd check
Alexander Popov [Fri, 9 Dec 2022 17:37:49 +0000 (20:37 +0300)]
Add the ssbd check

23 months agoAvoid the YAML parsing mistake
Alexander Popov [Thu, 8 Dec 2022 22:38:05 +0000 (01:38 +0300)]
Avoid the YAML parsing mistake

3.10 is parsed as a number and it is trimmed to 3.1.
That is expected behavior for numbers, but it's crazy for versions.

23 months agoFix `python-version` in the GitHub Actions
Alexander Popov [Thu, 8 Dec 2022 22:29:03 +0000 (01:29 +0300)]
Fix `python-version` in the GitHub Actions

Current `ubuntu-latest` (Ubuntu 22.04 for x86_64) provides the following
versions of Python:
 - 3.10.8
 - 3.11.0
 - 3.7.15
 - 3.8.15
 - 3.9.15

23 months agoReorder some checks, no functional changes
Alexander Popov [Thu, 8 Dec 2022 21:59:38 +0000 (00:59 +0300)]
Reorder some checks, no functional changes

23 months agoAdd the srbds check
Alexander Popov [Thu, 17 Nov 2022 16:39:32 +0000 (19:39 +0300)]
Add the srbds check

23 months agoAdd the retbleed check
Alexander Popov [Thu, 17 Nov 2022 16:29:26 +0000 (19:29 +0300)]
Add the retbleed check

23 months agoAdd the mmio_stale_data check
Alexander Popov [Thu, 17 Nov 2022 16:23:55 +0000 (19:23 +0300)]
Add the mmio_stale_data check

23 months agoAdd the tsx_async_abort check
Alexander Popov [Thu, 17 Nov 2022 16:19:55 +0000 (19:19 +0300)]
Add the tsx_async_abort check

23 months agoAdd the mds check
Alexander Popov [Thu, 17 Nov 2022 14:34:24 +0000 (17:34 +0300)]
Add the mds check

23 months agoAdd the l1tf check
Alexander Popov [Thu, 17 Nov 2022 14:28:28 +0000 (17:28 +0300)]
Add the l1tf check

23 months agoAdd the spectre_v2_user check
Alexander Popov [Thu, 17 Nov 2022 14:19:21 +0000 (17:19 +0300)]
Add the spectre_v2_user check

23 months agoDo refactoring in normalize_cmdline_options()
Alexander Popov [Thu, 17 Nov 2022 13:57:25 +0000 (16:57 +0300)]
Do refactoring in normalize_cmdline_options()

23 months agoAdd the spec_store_bypass_disable check
Alexander Popov [Thu, 17 Nov 2022 13:56:18 +0000 (16:56 +0300)]
Add the spec_store_bypass_disable check

23 months agoAdd the spectre_v2 check
Alexander Popov [Thu, 17 Nov 2022 13:42:30 +0000 (16:42 +0300)]
Add the spectre_v2 check

23 months agoIntroduce the 'is present' check instead of expected=None constructor parameter
Alexander Popov [Thu, 17 Nov 2022 12:23:55 +0000 (15:23 +0300)]
Introduce the 'is present' check instead of expected=None constructor parameter

2 years agoAdd the 'mitigations' check
Alexander Popov [Fri, 11 Nov 2022 14:39:19 +0000 (17:39 +0300)]
Add the 'mitigations' check

The default value for the 'mitigations' option is 'auto'.

So this option should be enabled ('is not off') or not set at all.

2 years agoAdd the nosmt check
Alexander Popov [Wed, 9 Nov 2022 15:32:52 +0000 (18:32 +0300)]
Add the nosmt check

2 years agoAdd a special 'desired val' -- 'is not off'
Alexander Popov [Wed, 9 Nov 2022 15:24:52 +0000 (18:24 +0300)]
Add a special 'desired val' -- 'is not off'

This check gives FAIL if the option value is 'off' or
the option is not found. In other cases this check gives OK.

This feature is needed for checking that the CPU vulnerability mitigations
are not disabled. Let's see how it works and maybe improve it in future.

2 years agoImprove the result descriptions
Alexander Popov [Wed, 9 Nov 2022 14:46:38 +0000 (17:46 +0300)]
Improve the result descriptions

2 years agoAdd assertions to check arguments of the Class constructors
Alexander Popov [Tue, 8 Nov 2022 21:31:16 +0000 (00:31 +0300)]
Add assertions to check arguments of the Class constructors

2 years agoUpdate the README
Alexander Popov [Sun, 23 Oct 2022 17:08:29 +0000 (20:08 +0300)]
Update the README

2 years agoAdd the ARM64_E0PD check
Alexander Popov [Sun, 23 Oct 2022 16:31:16 +0000 (19:31 +0300)]
Add the ARM64_E0PD check

2 years agoFix the SCHED_CORE check: it's now available for ARM64 and ARM
Alexander Popov [Sun, 23 Oct 2022 16:14:46 +0000 (19:14 +0300)]
Fix the SCHED_CORE check: it's now available for ARM64 and ARM

2 years agoUpdate the self-protection checks adopted by KSPP (part V)
Alexander Popov [Sun, 23 Oct 2022 15:23:55 +0000 (18:23 +0300)]
Update the self-protection checks adopted by KSPP (part V)

Thanks to @kees

2 years agoUpdate the self-protection checks adopted by KSPP (part IV): IOMMU
Alexander Popov [Sat, 22 Oct 2022 21:05:45 +0000 (00:05 +0300)]
Update the self-protection checks adopted by KSPP (part IV): IOMMU

Thanks to @kees

2 years agoUpdate the self-protection checks adopted by KSPP (part III)
Alexander Popov [Sat, 22 Oct 2022 21:02:55 +0000 (00:02 +0300)]
Update the self-protection checks adopted by KSPP (part III)

Thanks to @kees

2 years agoUpdate the KSPP recommendations again
Alexander Popov [Sat, 22 Oct 2022 18:34:56 +0000 (21:34 +0300)]
Update the KSPP recommendations again

2 years agoUpdate the self-protection checks adopted by KSPP (part II)
Alexander Popov [Thu, 13 Oct 2022 16:33:11 +0000 (19:33 +0300)]
Update the self-protection checks adopted by KSPP (part II)

Thanks to @kees

2 years agoUpdate the self-protection checks adopted by KSPP (part I)
Alexander Popov [Thu, 13 Oct 2022 15:24:41 +0000 (18:24 +0300)]
Update the self-protection checks adopted by KSPP (part I)

Thanks to @kees

2 years agoUpdate the HW_RANDOM_TPM check
Alexander Popov [Thu, 13 Oct 2022 15:07:14 +0000 (18:07 +0300)]
Update the HW_RANDOM_TPM check

Clip OS says that RANDOM_TRUST_BOOTLOADER and RANDOM_TRUST_CPU should be
disabled if HW_RANDOM_TPM is enabled. The Clip OS description:
  Do not credit entropy included in Linux’s entropy pool when generated
  by the CPU manufacturer’s HWRNG, the bootloader or the UEFI firmware.
  Fast and robust initialization of Linux’s CSPRNG is instead achieved
  thanks to the TPM’s HWRNG.

At the same time KSPP recommends to enable RANDOM_TRUST_BOOTLOADER and
RANDOM_TRUST_CPU anyway:
  Get as much entropy as possible from external sources. The Chacha mixer
  isn't vulnerable to injected entropy, so even malicious sources
  should not cause problems.

In this situation, I think kconfig-hardened-check should check
only HW_RANDOM_TPM (there is no contradiction about it)
and leave the decision about RANDOM_TRUST_BOOTLOADER and
RANDOM_TRUST_CPU to the owner of the system.

2 years agoUpdate the UBSAN checks according to the KSPP recommendations
Alexander Popov [Thu, 13 Oct 2022 14:19:23 +0000 (17:19 +0300)]
Update the UBSAN checks according to the KSPP recommendations

Thanks to @kees

2 years agoUpdate the security policy checks adopted by KSPP
Alexander Popov [Thu, 13 Oct 2022 13:54:02 +0000 (16:54 +0300)]
Update the security policy checks adopted by KSPP

Thanks to @kees

2 years agoUpdate the KSPP recommendations
Alexander Popov [Thu, 13 Oct 2022 13:17:58 +0000 (16:17 +0300)]
Update the KSPP recommendations

2 years agoImprove the README
Alexander Popov [Wed, 12 Oct 2022 18:49:23 +0000 (21:49 +0300)]
Improve the README

2 years agoUpdate the README
Alexander Popov [Sun, 9 Oct 2022 22:10:48 +0000 (01:10 +0300)]
Update the README

2 years agoDrop some of my security policy recommendations
Alexander Popov [Sun, 9 Oct 2022 21:55:21 +0000 (00:55 +0300)]
Drop some of my security policy recommendations

2 years agoCheck SECURITY_SELINUX_DEVELOP (recommended by Clip OS)
Alexander Popov [Sun, 9 Oct 2022 18:31:25 +0000 (21:31 +0300)]
Check SECURITY_SELINUX_DEVELOP (recommended by Clip OS)

Clip OS description: it "will eventually be n".

2 years agoCheck SECURITY_SELINUX_BOOTPARAM (recommended by Clip OS)
Alexander Popov [Sun, 9 Oct 2022 18:25:33 +0000 (21:25 +0300)]
Check SECURITY_SELINUX_BOOTPARAM (recommended by Clip OS)

2 years agoImprove the HW_RANDOM_TPM check
Alexander Popov [Sun, 9 Oct 2022 18:04:19 +0000 (21:04 +0300)]
Improve the HW_RANDOM_TPM check

RANDOM_TRUST_BOOTLOADER and RANDOM_TRUST_CPU should be disabled if
HW_RANDOM_TPM is enabled.

The Clip OS description:
Do not credit entropy included in Linux’s entropy pool when generated
by the CPU manufacturer’s HWRNG, the bootloader or the UEFI firmware.
Fast and robust initialization of Linux’s CSPRNG is instead achieved
thanks to the TPM’s HWRNG.

2 years agoCheck COREDUMP (recommended by Clip OS)
Alexander Popov [Sun, 9 Oct 2022 17:49:58 +0000 (20:49 +0300)]
Check COREDUMP (recommended by Clip OS)

Disabling COREDUMP is needed for cutting userspace attack surface.

2 years agoCheck CONFIG_HW_RANDOM_TPM (recommended by Clip OS)
Alexander Popov [Sun, 9 Oct 2022 12:49:13 +0000 (15:49 +0300)]
Check CONFIG_HW_RANDOM_TPM (recommended by Clip OS)

2 years agoCheck X86_MCE, X86_MCE_INTEL, X86_MCE_AMD (recommended by Clip OS)
Alexander Popov [Sun, 9 Oct 2022 12:32:55 +0000 (15:32 +0300)]
Check X86_MCE, X86_MCE_INTEL, X86_MCE_AMD (recommended by Clip OS)

These options are enabled by default.

2 years agoImprove the README
Alexander Popov [Sun, 9 Oct 2022 11:23:20 +0000 (14:23 +0300)]
Improve the README

2 years agoUpdate the README
Alexander Popov [Wed, 5 Oct 2022 13:56:28 +0000 (16:56 +0300)]
Update the README

2 years agoAlso check 'nospectre_v2' with 'spectre_v2'
Alexander Popov [Sun, 2 Oct 2022 18:45:13 +0000 (21:45 +0300)]
Also check 'nospectre_v2' with 'spectre_v2'

2 years agoChange the reason for the 'nopti' check
Alexander Popov [Sun, 2 Oct 2022 18:44:47 +0000 (21:44 +0300)]
Change the reason for the 'nopti' check

2 years agoChange the reason for the 'nokaslr' check
Alexander Popov [Sun, 2 Oct 2022 17:52:47 +0000 (20:52 +0300)]
Change the reason for the 'nokaslr' check

KASLR is enabled by default.

2 years agoAdd the 'spectre_v2' check
Alexander Popov [Sun, 2 Oct 2022 11:27:03 +0000 (14:27 +0300)]
Add the 'spectre_v2' check

Don't normalize this cmdline option.

2 years agoAdd the 'nospectre_v2' check
Alexander Popov [Sun, 2 Oct 2022 11:23:19 +0000 (14:23 +0300)]
Add the 'nospectre_v2' check

2 years agoChange the reason for the 'nosmep' and 'nosmap' checks
Alexander Popov [Sun, 2 Oct 2022 11:04:10 +0000 (14:04 +0300)]
Change the reason for the 'nosmep' and 'nosmap' checks

SMEP and SMAP are enabled by default.

2 years agoAdd the 'nospectre_v1' check
Alexander Popov [Sun, 2 Oct 2022 10:39:38 +0000 (13:39 +0300)]
Add the 'nospectre_v1' check

2 years agoAdd the 'nopti' check
Alexander Popov [Sun, 2 Oct 2022 10:20:11 +0000 (13:20 +0300)]
Add the 'nopti' check

2 years agoAdd the comments: CC_IS_GCC and CC_IS_CLANG exist since v4.18
Alexander Popov [Sat, 24 Sep 2022 22:12:55 +0000 (01:12 +0300)]
Add the comments: CC_IS_GCC and CC_IS_CLANG exist since v4.18

2 years agoAdd the UBSAN_LOCAL_BOUNDS check for Clang build
Alexander Popov [Sat, 24 Sep 2022 21:51:25 +0000 (00:51 +0300)]
Add the UBSAN_LOCAL_BOUNDS check for Clang build

Explanations from the Linux kernel commit 6a6155f664e31c9be43cd:

When the kernel is compiled with Clang, -fsanitize=bounds expands to
-fsanitize=array-bounds and -fsanitize=local-bounds.

Enabling -fsanitize=local-bounds with Clang has the side-effect of
inserting traps.

That's why UBSAN_LOCAL_BOUNDS can enable the 'local-bounds' option
only when UBSAN_TRAP is enabled.

2 years agoUpdate the links to AOSP and GKI
Alexander Popov [Sun, 18 Sep 2022 13:02:23 +0000 (16:02 +0300)]
Update the links to AOSP and GKI

Android Open Source Project (AOSP):
https://source.android.com/docs/setup/build/building-kernels

Android Generic Kernel Image (GKI):
https://source.android.com/docs/core/architecture/kernel/gki-release-builds

Also add the GKI config `android13-5.10`.

Thanks to @h0t for the idea.