Merge 6.12.29 into android16-6.12-lts
GKI (arm64) relevant 44 out of 185 changes, affecting 54 files +634/-365b32411f045dm: add missing unlock on in dm_keyslot_evict() [1 file, +2/-1]61e0fc3312fs/erofs/fileio: call erofs_onlinefolio_split() after bio_add_folio() [1 file, +2/-2]14ee85b748firmware: arm_scmi: Fix timeout checks on polling path [1 file, +8/-5]98cd7ed927sch_htb: make htb_deactivate() idempotent [1 file, +6/-9]35be4c0cdfgre: Fix again IPv6 link-local address generation. [1 file, +9/-6]c33927f385can: gw: fix RCU/BH usage in cgw_create_job() [1 file, +90/-59]4555c4a13awifi: mac80211: fix the type of status_code for negotiated TID to Link Mapping [2 files, +7/-7]64385c0d02erofs: ensure the extra temporary copy is valid for shortened bvecs [1 file, +14/-17]b37e54259cbpf: Scrub packet on bpf_redirect_peer [1 file, +1/-0]bb8f86f40enet: export a helper for adding up queue stats [2 files, +56/-19]302a0cd0bbInput: xpad - fix Share button on Xbox One controllers [1 file, +20/-15]bf239d3835Input: xpad - add support for 8BitDo Ultimate 2 Wireless Controller [1 file, +1/-0]38bb0170d6Input: xpad - fix two controller table values [1 file, +2/-2]2910019b04mm: vmalloc: support more granular vrealloc() sizing [2 files, +25/-7]6166c3cf40mm/huge_memory: fix dereferencing invalid pmd migration entry [1 file, +8/-3]b543a5a73bmm/userfaultfd: fix uninitialized output field for -EAGAIN race [1 file, +22/-6]7f37e31483io_uring: ensure deferred completions are flushed for multishot [1 file, +8/-0]abbc99e898arm64: cpufeature: Move arm64_use_ng_mappings to the .data section to prevent wrong idmap generation [1 file, +8/-1]d66a22f6a4memblock: Accept allocated memory before use in memblock_double_array() [1 file, +8/-1]d63851049fmodule: ensure that kobject_put() is safe for module type kobjects [1 file, +3/-1]75f23e49adusb: gadget: f_ecm: Add get_status callback [1 file, +7/-0]d1c8fa4c6eusb: gadget: Use get_status callback to set remote wakeup capability [1 file, +5/-7]3366a19948usb: typec: tcpm: delay SNK_TRY_WAIT_DEBOUNCE to SRC_TRYWAIT transition [1 file, +1/-1]5ad298d6d4usb: typec: ucsi: displayport: Fix NULL pointer access [1 file, +2/-0]afe8849597types: Complement the aligned types with signed 64-bit one [2 files, +3/-1]02a77b3020loop: Use bdev limit helpers for configuring discard [1 file, +4/-4]722f6dece7loop: Simplify discard granularity calc [1 file, +1/-2]0558ce095bloop: Fix ABBA locking race [1 file, +15/-15]5e1470b276loop: refactor queue limits updates [1 file, +20/-16]a781ffe410loop: factor out a loop_assign_backing_file helper [1 file, +10/-10]184b147b9floop: Add sanity check for read/write_iter [1 file, +23/-0]19fa2a4830nvme: unblock ctrl state transition for firmware update [1 file, +2/-1]3edac2949eio_uring/sqpoll: Increase task_work submission batch size [1 file, +1/-1]cd010271a9do_umount(): add missing barrier before refcount checks in sync case [1 file, +2/-1]2482f7705bio_uring: always arm linked timeouts prior to issue [1 file, +15/-35]564d25b1a6mm: page_alloc: don't steal single pages from biggest buddy [1 file, +34/-46]16bae58f73mm: page_alloc: speed up fallbacks in rmqueue_bulk() [1 file, +80/-33]86b37810fasched/eevdf: Fix se->slice being set to U64_MAX and resulting crash [1 file, +1/-3]2a3915e861arm64: insn: Add support for encoding DSB [2 files, +38/-23]ec5bca57afarm64: proton-pack: Expose whether the platform is mitigated by firmware [2 files, +6/-0]f2aebb8ec6arm64: proton-pack: Expose whether the branchy loop k value [2 files, +6/-0]38c345fd54arm64: bpf: Add BHB mitigation to the epilogue for cBPF programs [3 files, +52/-5]e5f5100f1carm64: bpf: Only mitigate cBPF programs loaded by unprivileged users [1 file, +3/-0]2176530849arm64: proton-pack: Add new CPUs 'k' values for branch mitigation [2 files, +3/-0] Changes in 6.12.29 dm: add missing unlock on in dm_keyslot_evict() fs/erofs/fileio: call erofs_onlinefolio_split() after bio_add_folio() Revert "btrfs: canonicalize the device path before adding it" arm64: dts: imx8mm-verdin: Link reg_usdhc2_vqmmc to usdhc2 firmware: arm_scmi: Fix timeout checks on polling path can: mcan: m_can_class_unregister(): fix order of unregistration calls s390/pci: Fix missing check for zpci_create_device() error return wifi: cfg80211: fix out-of-bounds access during multi-link element defragmentation vfio/pci: Align huge faults to order s390/pci: Fix duplicate pci_dev_put() in disable_slot() when PF has child VFs can: mcp251xfd: mcp251xfd_remove(): fix order of unregistration calls can: rockchip_canfd: rkcanfd_remove(): fix order of unregistration calls ksmbd: prevent rename with empty string ksmbd: prevent out-of-bounds stream writes by validating *pos ksmbd: Fix UAF in __close_file_table_ids openvswitch: Fix unsafe attribute parsing in output_userspace() ksmbd: fix memory leak in parse_lease_state() s390/entry: Fix last breaking event handling in case of stack corruption sch_htb: make htb_deactivate() idempotent virtio_net: xsk: bind/unbind xsk for tx virtio-net: free xsk_buffs on error in virtnet_xsk_pool_enable() gre: Fix again IPv6 link-local address generation. net: ethernet: mtk_eth_soc: reset all TX queues on DMA free net: ethernet: mtk_eth_soc: do not reset PSE when setting FE can: m_can: m_can_class_allocate_dev(): initialize spin lock on device probe can: mcp251xfd: fix TDC setting for low data bit rates can: gw: fix RCU/BH usage in cgw_create_job() wifi: mac80211: fix the type of status_code for negotiated TID to Link Mapping ice: Initial support for E825C hardware in ice_adapter ice: use DSN instead of PCI BDF for ice_adapter index erofs: ensure the extra temporary copy is valid for shortened bvecs ipvs: fix uninit-value for saddr in do_output_route4 netfilter: ipset: fix region locking in hash types bpf: Scrub packet on bpf_redirect_peer net: dsa: b53: allow leaky reserved multicast net: dsa: b53: keep CPU port always tagged again net: dsa: b53: fix clearing PVID of a port net: dsa: b53: fix flushing old pvid VLAN on pvid change net: dsa: b53: fix VLAN ID for untagged vlan on bridge leave net: dsa: b53: always rejoin default untagged VLAN on bridge leave net: dsa: b53: do not allow to configure VLAN 0 net: dsa: b53: do not program vlans when vlan filtering is off net: dsa: b53: fix toggling vlan_filtering net: dsa: b53: fix learning on VLAN unaware bridges net: dsa: b53: do not set learning and unicast/multicast on up fbnic: Fix initialization of mailbox descriptor rings fbnic: Gate AXI read/write enabling on FW mailbox fbnic: Actually flush_tx instead of stalling out fbnic: Improve responsiveness of fbnic_mbx_poll_tx_ready fbnic: Pull fbnic_fw_xmit_cap_msg use out of interrupt context fbnic: Do not allow mailbox to toggle to ready outside fbnic_mbx_poll_tx_ready net: export a helper for adding up queue stats virtio-net: fix total qstat values Input: cyttsp5 - ensure minimum reset pulse width Input: cyttsp5 - fix power control issue on wakeup Input: mtk-pmic-keys - fix possible null pointer dereference Input: xpad - fix Share button on Xbox One controllers Input: xpad - add support for 8BitDo Ultimate 2 Wireless Controller Input: xpad - fix two controller table values Input: synaptics - enable InterTouch on Dynabook Portege X30-D Input: synaptics - enable InterTouch on Dynabook Portege X30L-G Input: synaptics - enable InterTouch on Dell Precision M3800 Input: synaptics - enable SMBus for HP Elitebook 850 G1 Input: synaptics - enable InterTouch on TUXEDO InfinityBook Pro 14 v5 rust: clean Rust 1.88.0's `unnecessary_transmutes` lint objtool/rust: add one more `noreturn` Rust function for Rust 1.87.0 rust: clean Rust 1.88.0's warning about `clippy::disallowed_macros` configuration staging: iio: adc: ad7816: Correct conditional logic for store mode staging: bcm2835-camera: Initialise dev in v4l2_dev staging: axis-fifo: Remove hardware resets for user errors staging: axis-fifo: Correct handling of tx_fifo_depth for size validation x86/mm: Eliminate window where TLB flushes may be inadvertently skipped mm: fix folio_pte_batch() on XEN PV mm: vmalloc: support more granular vrealloc() sizing mm/huge_memory: fix dereferencing invalid pmd migration entry mm/userfaultfd: fix uninitialized output field for -EAGAIN race selftests/mm: compaction_test: support platform with huge mount of memory selftests/mm: fix a build failure on powerpc KVM: SVM: Forcibly leave SMM mode on SHUTDOWN interception drm/amd/display: Shift DMUB AUX reply command if necessary io_uring: ensure deferred completions are flushed for multishot iio: adc: ad7606: fix serial register access iio: adc: rockchip: Fix clock initialization sequence iio: adis16201: Correct inclinometer channel resolution iio: imu: inv_mpu6050: align buffer for timestamp iio: imu: st_lsm6dsx: fix possible lockup in st_lsm6dsx_read_fifo iio: imu: st_lsm6dsx: fix possible lockup in st_lsm6dsx_read_tagged_fifo drm/v3d: Add job to pending list if the reset was skipped drm/xe: Add page queue multiplier drm/amdgpu/vcn: using separate VCN1_AON_SOC offset drm/amd/display: Fix invalid context error in dml helper drm/amd/display: more liberal vmin/vmax update for freesync drm/amd/display: Fix the checking condition in dmub aux handling drm/amd/display: Remove incorrect checking in dmub aux handler drm/amd/display: Fix wrong handling for AUX_DEFER case drm/amd/display: Copy AUX read reply data whenever length > 0 drm/amdgpu/hdp4: use memcfg register to post the write for HDP flush drm/amdgpu/hdp5.2: use memcfg register to post the write for HDP flush drm/amdgpu/hdp5: use memcfg register to post the write for HDP flush drm/amdgpu/hdp6: use memcfg register to post the write for HDP flush drm/amdgpu/hdp7: use memcfg register to post the write for HDP flush usb: uhci-platform: Make the clock really optional smb: client: Avoid race in open_cached_dir with lease breaks xen: swiotlb: Use swiotlb bouncing if kmalloc allocation demands it xenbus: Use kref to track req lifetime accel/ivpu: Increase state dump msg timeout arm64: cpufeature: Move arm64_use_ng_mappings to the .data section to prevent wrong idmap generation clocksource/i8253: Use raw_spinlock_irqsave() in clockevent_i8253_disable() memblock: Accept allocated memory before use in memblock_double_array() module: ensure that kobject_put() is safe for module type kobjects x86/microcode: Consolidate the loader enablement checking ocfs2: fix the issue with discontiguous allocation in the global_bitmap ocfs2: switch osb->disable_recovery to enum ocfs2: implement handshaking with ocfs2 recovery thread ocfs2: stop quota recovery before disabling quotas usb: dwc3: gadget: Make gadget_wakeup asynchronous usb: cdnsp: Fix issue with resuming from L1 usb: cdnsp: fix L1 resume issue for RTL_REVISION_NEW_LPM version usb: gadget: f_ecm: Add get_status callback usb: gadget: tegra-xudc: ACK ST_RC after clearing CTRL_RUN usb: gadget: Use get_status callback to set remote wakeup capability usb: host: tegra: Prevent host controller crash when OTG port is used usb: misc: onboard_usb_dev: fix support for Cypress HX3 hubs usb: typec: tcpm: delay SNK_TRY_WAIT_DEBOUNCE to SRC_TRYWAIT transition usb: typec: ucsi: displayport: Fix NULL pointer access USB: usbtmc: use interruptible sleep in usbtmc_read usb: usbtmc: Fix erroneous get_stb ioctl error returns usb: usbtmc: Fix erroneous wait_srq ioctl return usb: usbtmc: Fix erroneous generic_read ioctl return iio: accel: adxl367: fix setting odr for activity time update iio: temp: maxim-thermocouple: Fix potential lack of DMA safe buffer. types: Complement the aligned types with signed 64-bit one iio: accel: adxl355: Make timestamp 64-bit aligned using aligned_s64 iio: adc: dln2: Use aligned_s64 for timestamp MIPS: Fix MAX_REG_OFFSET riscv: misaligned: Add handling for ZCB instructions loop: Use bdev limit helpers for configuring discard loop: Simplify discard granularity calc loop: Fix ABBA locking race loop: refactor queue limits updates loop: factor out a loop_assign_backing_file helper loop: Add sanity check for read/write_iter drm/panel: simple: Update timings for AUO G101EVN010 nvme: unblock ctrl state transition for firmware update riscv: misaligned: factorize trap handling riscv: misaligned: enable IRQs while handling misaligned accesses drm/xe/tests/mocs: Update xe_force_wake_get() return handling drm/xe/tests/mocs: Hold XE_FORCEWAKE_ALL for LNCF regs io_uring/sqpoll: Increase task_work submission batch size do_umount(): add missing barrier before refcount checks in sync case Revert "um: work around sched_yield not yielding in time-travel mode" rust: allow Rust 1.87.0's `clippy::ptr_eq` lint rust: clean Rust 1.88.0's `clippy::uninlined_format_args` lint io_uring: always arm linked timeouts prior to issue Bluetooth: btmtk: Remove resetting mt7921 before downloading the fw Bluetooth: btmtk: Remove the resetting step before downloading the fw mm: page_alloc: don't steal single pages from biggest buddy mm: page_alloc: speed up fallbacks in rmqueue_bulk() sched/eevdf: Fix se->slice being set to U64_MAX and resulting crash arm64: insn: Add support for encoding DSB arm64: proton-pack: Expose whether the platform is mitigated by firmware arm64: proton-pack: Expose whether the branchy loop k value arm64: bpf: Add BHB mitigation to the epilogue for cBPF programs arm64: bpf: Only mitigate cBPF programs loaded by unprivileged users arm64: proton-pack: Add new CPUs 'k' values for branch mitigation x86/bpf: Call branch history clearing sequence on exit x86/bpf: Add IBHF call at end of classic BPF x86/bhi: Do not set BHI_DIS_S in 32-bit mode x86/speculation: Simplify and make CALL_NOSPEC consistent x86/speculation: Add a conditional CS prefix to CALL_NOSPEC x86/speculation: Remove the extra #ifdef around CALL_NOSPEC Documentation: x86/bugs/its: Add ITS documentation x86/its: Enumerate Indirect Target Selection (ITS) bug x86/its: Add support for ITS-safe indirect thunk x86/its: Add support for ITS-safe return thunk x86/its: Enable Indirect Target Selection mitigation x86/its: Add "vmexit" option to skip mitigation on some CPUs x86/its: Add support for RSB stuffing mitigation x86/its: Align RETs in BHB clear sequence to avoid thunking x86/ibt: Keep IBT disabled during alternative patching x86/its: Use dynamic thunks for indirect branches selftest/x86/bugs: Add selftests for ITS x86/its: Fix build errors when CONFIG_MODULES=n x86/its: FineIBT-paranoid vs ITS Linux 6.12.29 Change-Id: I00ff9cc212474331d43028ec90a190dcd1dfa697 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
@@ -511,6 +511,7 @@ Description: information about CPUs heterogeneity.
|
||||
|
||||
What: /sys/devices/system/cpu/vulnerabilities
|
||||
/sys/devices/system/cpu/vulnerabilities/gather_data_sampling
|
||||
/sys/devices/system/cpu/vulnerabilities/indirect_target_selection
|
||||
/sys/devices/system/cpu/vulnerabilities/itlb_multihit
|
||||
/sys/devices/system/cpu/vulnerabilities/l1tf
|
||||
/sys/devices/system/cpu/vulnerabilities/mds
|
||||
|
||||
@@ -22,3 +22,4 @@ are configurable at compile, boot or run time.
|
||||
srso
|
||||
gather_data_sampling
|
||||
reg-file-data-sampling
|
||||
indirect-target-selection
|
||||
|
||||
168
Documentation/admin-guide/hw-vuln/indirect-target-selection.rst
Normal file
168
Documentation/admin-guide/hw-vuln/indirect-target-selection.rst
Normal file
@@ -0,0 +1,168 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Indirect Target Selection (ITS)
|
||||
===============================
|
||||
|
||||
ITS is a vulnerability in some Intel CPUs that support Enhanced IBRS and were
|
||||
released before Alder Lake. ITS may allow an attacker to control the prediction
|
||||
of indirect branches and RETs located in the lower half of a cacheline.
|
||||
|
||||
ITS is assigned CVE-2024-28956 with a CVSS score of 4.7 (Medium).
|
||||
|
||||
Scope of Impact
|
||||
---------------
|
||||
- **eIBRS Guest/Host Isolation**: Indirect branches in KVM/kernel may still be
|
||||
predicted with unintended target corresponding to a branch in the guest.
|
||||
|
||||
- **Intra-Mode BTI**: In-kernel training such as through cBPF or other native
|
||||
gadgets.
|
||||
|
||||
- **Indirect Branch Prediction Barrier (IBPB)**: After an IBPB, indirect
|
||||
branches may still be predicted with targets corresponding to direct branches
|
||||
executed prior to the IBPB. This is fixed by the IPU 2025.1 microcode, which
|
||||
should be available via distro updates. Alternatively microcode can be
|
||||
obtained from Intel's github repository [#f1]_.
|
||||
|
||||
Affected CPUs
|
||||
-------------
|
||||
Below is the list of ITS affected CPUs [#f2]_ [#f3]_:
|
||||
|
||||
======================== ============ ==================== ===============
|
||||
Common name Family_Model eIBRS Intra-mode BTI
|
||||
Guest/Host Isolation
|
||||
======================== ============ ==================== ===============
|
||||
SKYLAKE_X (step >= 6) 06_55H Affected Affected
|
||||
ICELAKE_X 06_6AH Not affected Affected
|
||||
ICELAKE_D 06_6CH Not affected Affected
|
||||
ICELAKE_L 06_7EH Not affected Affected
|
||||
TIGERLAKE_L 06_8CH Not affected Affected
|
||||
TIGERLAKE 06_8DH Not affected Affected
|
||||
KABYLAKE_L (step >= 12) 06_8EH Affected Affected
|
||||
KABYLAKE (step >= 13) 06_9EH Affected Affected
|
||||
COMETLAKE 06_A5H Affected Affected
|
||||
COMETLAKE_L 06_A6H Affected Affected
|
||||
ROCKETLAKE 06_A7H Not affected Affected
|
||||
======================== ============ ==================== ===============
|
||||
|
||||
- All affected CPUs enumerate Enhanced IBRS feature.
|
||||
- IBPB isolation is affected on all ITS affected CPUs, and need a microcode
|
||||
update for mitigation.
|
||||
- None of the affected CPUs enumerate BHI_CTRL which was introduced in Golden
|
||||
Cove (Alder Lake and Sapphire Rapids). This can help guests to determine the
|
||||
host's affected status.
|
||||
- Intel Atom CPUs are not affected by ITS.
|
||||
|
||||
Mitigation
|
||||
----------
|
||||
As only the indirect branches and RETs that have their last byte of instruction
|
||||
in the lower half of the cacheline are vulnerable to ITS, the basic idea behind
|
||||
the mitigation is to not allow indirect branches in the lower half.
|
||||
|
||||
This is achieved by relying on existing retpoline support in the kernel, and in
|
||||
compilers. ITS-vulnerable retpoline sites are runtime patched to point to newly
|
||||
added ITS-safe thunks. These safe thunks consists of indirect branch in the
|
||||
second half of the cacheline. Not all retpoline sites are patched to thunks, if
|
||||
a retpoline site is evaluated to be ITS-safe, it is replaced with an inline
|
||||
indirect branch.
|
||||
|
||||
Dynamic thunks
|
||||
~~~~~~~~~~~~~~
|
||||
From a dynamically allocated pool of safe-thunks, each vulnerable site is
|
||||
replaced with a new thunk, such that they get a unique address. This could
|
||||
improve the branch prediction accuracy. Also, it is a defense-in-depth measure
|
||||
against aliasing.
|
||||
|
||||
Note, for simplicity, indirect branches in eBPF programs are always replaced
|
||||
with a jump to a static thunk in __x86_indirect_its_thunk_array. If required,
|
||||
in future this can be changed to use dynamic thunks.
|
||||
|
||||
All vulnerable RETs are replaced with a static thunk, they do not use dynamic
|
||||
thunks. This is because RETs get their prediction from RSB mostly that does not
|
||||
depend on source address. RETs that underflow RSB may benefit from dynamic
|
||||
thunks. But, RETs significantly outnumber indirect branches, and any benefit
|
||||
from a unique source address could be outweighed by the increased icache
|
||||
footprint and iTLB pressure.
|
||||
|
||||
Retpoline
|
||||
~~~~~~~~~
|
||||
Retpoline sequence also mitigates ITS-unsafe indirect branches. For this
|
||||
reason, when retpoline is enabled, ITS mitigation only relocates the RETs to
|
||||
safe thunks. Unless user requested the RSB-stuffing mitigation.
|
||||
|
||||
RSB Stuffing
|
||||
~~~~~~~~~~~~
|
||||
RSB-stuffing via Call Depth Tracking is a mitigation for Retbleed RSB-underflow
|
||||
attacks. And it also mitigates RETs that are vulnerable to ITS.
|
||||
|
||||
Mitigation in guests
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
All guests deploy ITS mitigation by default, irrespective of eIBRS enumeration
|
||||
and Family/Model of the guest. This is because eIBRS feature could be hidden
|
||||
from a guest. One exception to this is when a guest enumerates BHI_DIS_S, which
|
||||
indicates that the guest is running on an unaffected host.
|
||||
|
||||
To prevent guests from unnecessarily deploying the mitigation on unaffected
|
||||
platforms, Intel has defined ITS_NO bit(62) in MSR IA32_ARCH_CAPABILITIES. When
|
||||
a guest sees this bit set, it should not enumerate the ITS bug. Note, this bit
|
||||
is not set by any hardware, but is **intended for VMMs to synthesize** it for
|
||||
guests as per the host's affected status.
|
||||
|
||||
Mitigation options
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
The ITS mitigation can be controlled using the "indirect_target_selection"
|
||||
kernel parameter. The available options are:
|
||||
|
||||
======== ===================================================================
|
||||
on (default) Deploy the "Aligned branch/return thunks" mitigation.
|
||||
If spectre_v2 mitigation enables retpoline, aligned-thunks are only
|
||||
deployed for the affected RET instructions. Retpoline mitigates
|
||||
indirect branches.
|
||||
|
||||
off Disable ITS mitigation.
|
||||
|
||||
vmexit Equivalent to "=on" if the CPU is affected by guest/host isolation
|
||||
part of ITS. Otherwise, mitigation is not deployed. This option is
|
||||
useful when host userspace is not in the threat model, and only
|
||||
attacks from guest to host are considered.
|
||||
|
||||
stuff Deploy RSB-fill mitigation when retpoline is also deployed.
|
||||
Otherwise, deploy the default mitigation. When retpoline mitigation
|
||||
is enabled, RSB-stuffing via Call-Depth-Tracking also mitigates
|
||||
ITS.
|
||||
|
||||
force Force the ITS bug and deploy the default mitigation.
|
||||
======== ===================================================================
|
||||
|
||||
Sysfs reporting
|
||||
---------------
|
||||
|
||||
The sysfs file showing ITS mitigation status is:
|
||||
|
||||
/sys/devices/system/cpu/vulnerabilities/indirect_target_selection
|
||||
|
||||
Note, microcode mitigation status is not reported in this file.
|
||||
|
||||
The possible values in this file are:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - Not affected
|
||||
- The processor is not vulnerable.
|
||||
* - Vulnerable
|
||||
- System is vulnerable and no mitigation has been applied.
|
||||
* - Vulnerable, KVM: Not affected
|
||||
- System is vulnerable to intra-mode BTI, but not affected by eIBRS
|
||||
guest/host isolation.
|
||||
* - Mitigation: Aligned branch/return thunks
|
||||
- The mitigation is enabled, affected indirect branches and RETs are
|
||||
relocated to safe thunks.
|
||||
* - Mitigation: Retpolines, Stuffing RSB
|
||||
- The mitigation is enabled using retpoline and RSB stuffing.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [#f1] Microcode repository - https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files
|
||||
|
||||
.. [#f2] Affected Processors list - https://www.intel.com/content/www/us/en/developer/topic-technology/software-security-guidance/processors-affected-consolidated-product-cpu-model.html
|
||||
|
||||
.. [#f3] Affected Processors list (machine readable) - https://github.com/intel/Intel-affected-processor-list
|
||||
@@ -2157,6 +2157,23 @@
|
||||
different crypto accelerators. This option can be used
|
||||
to achieve best performance for particular HW.
|
||||
|
||||
indirect_target_selection= [X86,Intel] Mitigation control for Indirect
|
||||
Target Selection(ITS) bug in Intel CPUs. Updated
|
||||
microcode is also required for a fix in IBPB.
|
||||
|
||||
on: Enable mitigation (default).
|
||||
off: Disable mitigation.
|
||||
force: Force the ITS bug and deploy default
|
||||
mitigation.
|
||||
vmexit: Only deploy mitigation if CPU is affected by
|
||||
guest/host isolation part of ITS.
|
||||
stuff: Deploy RSB-fill mitigation when retpoline is
|
||||
also deployed. Otherwise, deploy the default
|
||||
mitigation.
|
||||
|
||||
For details see:
|
||||
Documentation/admin-guide/hw-vuln/indirect-target-selection.rst
|
||||
|
||||
init= [KNL]
|
||||
Format: <full_path>
|
||||
Run specified binary instead of /sbin/init as init
|
||||
@@ -3536,6 +3553,7 @@
|
||||
expose users to several CPU vulnerabilities.
|
||||
Equivalent to: if nokaslr then kpti=0 [ARM64]
|
||||
gather_data_sampling=off [X86]
|
||||
indirect_target_selection=off [X86]
|
||||
kvm.nx_huge_pages=off [X86]
|
||||
l1tf=off [X86]
|
||||
mds=off [X86]
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
VERSION = 6
|
||||
PATCHLEVEL = 12
|
||||
SUBLEVEL = 28
|
||||
SUBLEVEL = 29
|
||||
EXTRAVERSION =
|
||||
NAME = Baby Opossum Posse
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#define ARM_CPU_PART_CORTEX_A78AE 0xD42
|
||||
#define ARM_CPU_PART_CORTEX_X1 0xD44
|
||||
#define ARM_CPU_PART_CORTEX_A510 0xD46
|
||||
#define ARM_CPU_PART_CORTEX_X1C 0xD4C
|
||||
#define ARM_CPU_PART_CORTEX_A520 0xD80
|
||||
#define ARM_CPU_PART_CORTEX_A710 0xD47
|
||||
#define ARM_CPU_PART_CORTEX_A715 0xD4D
|
||||
@@ -166,6 +167,7 @@
|
||||
#define MIDR_CORTEX_A78AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78AE)
|
||||
#define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1)
|
||||
#define MIDR_CORTEX_A510 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A510)
|
||||
#define MIDR_CORTEX_X1C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1C)
|
||||
#define MIDR_CORTEX_A520 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A520)
|
||||
#define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710)
|
||||
#define MIDR_CORTEX_A715 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A715)
|
||||
|
||||
@@ -693,6 +693,7 @@ u32 aarch64_insn_gen_cas(enum aarch64_insn_register result,
|
||||
}
|
||||
#endif
|
||||
u32 aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type);
|
||||
u32 aarch64_insn_gen_dsb(enum aarch64_insn_mb_type type);
|
||||
u32 aarch64_insn_gen_mrs(enum aarch64_insn_register result,
|
||||
enum aarch64_insn_system_register sysreg);
|
||||
|
||||
|
||||
@@ -97,6 +97,9 @@ enum mitigation_state arm64_get_meltdown_state(void);
|
||||
|
||||
enum mitigation_state arm64_get_spectre_bhb_state(void);
|
||||
bool is_spectre_bhb_affected(const struct arm64_cpu_capabilities *entry, int scope);
|
||||
extern bool __nospectre_bhb;
|
||||
u8 get_spectre_bhb_loop_value(void);
|
||||
bool is_spectre_bhb_fw_mitigated(void);
|
||||
void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
||||
bool try_emulate_el1_ssbs(struct pt_regs *regs, u32 instr);
|
||||
|
||||
|
||||
@@ -891,6 +891,7 @@ static u8 spectre_bhb_loop_affected(void)
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_A78AE),
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_A78C),
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_X1),
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_X1C),
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_A710),
|
||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_X2),
|
||||
MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2),
|
||||
@@ -998,6 +999,11 @@ bool is_spectre_bhb_affected(const struct arm64_cpu_capabilities *entry,
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 get_spectre_bhb_loop_value(void)
|
||||
{
|
||||
return max_bhb_k;
|
||||
}
|
||||
|
||||
static void this_cpu_set_vectors(enum arm64_bp_harden_el1_vectors slot)
|
||||
{
|
||||
const char *v = arm64_get_bp_hardening_vector(slot);
|
||||
@@ -1015,7 +1021,7 @@ static void this_cpu_set_vectors(enum arm64_bp_harden_el1_vectors slot)
|
||||
isb();
|
||||
}
|
||||
|
||||
static bool __read_mostly __nospectre_bhb;
|
||||
bool __read_mostly __nospectre_bhb;
|
||||
static int __init parse_spectre_bhb_param(char *str)
|
||||
{
|
||||
__nospectre_bhb = true;
|
||||
@@ -1093,6 +1099,11 @@ void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry)
|
||||
update_mitigation_state(&spectre_bhb_state, state);
|
||||
}
|
||||
|
||||
bool is_spectre_bhb_fw_mitigated(void)
|
||||
{
|
||||
return test_bit(BHB_FW, &system_bhb_mitigations);
|
||||
}
|
||||
|
||||
/* Patched to NOP when enabled */
|
||||
void noinstr spectre_bhb_patch_loop_mitigation_enable(struct alt_instr *alt,
|
||||
__le32 *origptr,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*
|
||||
* Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com>
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/printk.h>
|
||||
@@ -1471,43 +1472,41 @@ u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant,
|
||||
return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, Rm);
|
||||
}
|
||||
|
||||
static u32 __get_barrier_crm_val(enum aarch64_insn_mb_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case AARCH64_INSN_MB_SY:
|
||||
return 0xf;
|
||||
case AARCH64_INSN_MB_ST:
|
||||
return 0xe;
|
||||
case AARCH64_INSN_MB_LD:
|
||||
return 0xd;
|
||||
case AARCH64_INSN_MB_ISH:
|
||||
return 0xb;
|
||||
case AARCH64_INSN_MB_ISHST:
|
||||
return 0xa;
|
||||
case AARCH64_INSN_MB_ISHLD:
|
||||
return 0x9;
|
||||
case AARCH64_INSN_MB_NSH:
|
||||
return 0x7;
|
||||
case AARCH64_INSN_MB_NSHST:
|
||||
return 0x6;
|
||||
case AARCH64_INSN_MB_NSHLD:
|
||||
return 0x5;
|
||||
default:
|
||||
pr_err("%s: unknown barrier type %d\n", __func__, type);
|
||||
return AARCH64_BREAK_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
u32 aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type)
|
||||
{
|
||||
u32 opt;
|
||||
u32 insn;
|
||||
|
||||
switch (type) {
|
||||
case AARCH64_INSN_MB_SY:
|
||||
opt = 0xf;
|
||||
break;
|
||||
case AARCH64_INSN_MB_ST:
|
||||
opt = 0xe;
|
||||
break;
|
||||
case AARCH64_INSN_MB_LD:
|
||||
opt = 0xd;
|
||||
break;
|
||||
case AARCH64_INSN_MB_ISH:
|
||||
opt = 0xb;
|
||||
break;
|
||||
case AARCH64_INSN_MB_ISHST:
|
||||
opt = 0xa;
|
||||
break;
|
||||
case AARCH64_INSN_MB_ISHLD:
|
||||
opt = 0x9;
|
||||
break;
|
||||
case AARCH64_INSN_MB_NSH:
|
||||
opt = 0x7;
|
||||
break;
|
||||
case AARCH64_INSN_MB_NSHST:
|
||||
opt = 0x6;
|
||||
break;
|
||||
case AARCH64_INSN_MB_NSHLD:
|
||||
opt = 0x5;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unknown dmb type %d\n", __func__, type);
|
||||
opt = __get_barrier_crm_val(type);
|
||||
if (opt == AARCH64_BREAK_FAULT)
|
||||
return AARCH64_BREAK_FAULT;
|
||||
}
|
||||
|
||||
insn = aarch64_insn_get_dmb_value();
|
||||
insn &= ~GENMASK(11, 8);
|
||||
@@ -1516,6 +1515,21 @@ u32 aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type)
|
||||
return insn;
|
||||
}
|
||||
|
||||
u32 aarch64_insn_gen_dsb(enum aarch64_insn_mb_type type)
|
||||
{
|
||||
u32 opt, insn;
|
||||
|
||||
opt = __get_barrier_crm_val(type);
|
||||
if (opt == AARCH64_BREAK_FAULT)
|
||||
return AARCH64_BREAK_FAULT;
|
||||
|
||||
insn = aarch64_insn_get_dsb_base_value();
|
||||
insn &= ~GENMASK(11, 8);
|
||||
insn |= (opt << 8);
|
||||
|
||||
return insn;
|
||||
}
|
||||
|
||||
u32 aarch64_insn_gen_mrs(enum aarch64_insn_register result,
|
||||
enum aarch64_insn_system_register sysreg)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "bpf_jit: " fmt
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/filter.h>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cfi.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/insn.h>
|
||||
#include <asm/patching.h>
|
||||
@@ -866,7 +868,51 @@ static void build_plt(struct jit_ctx *ctx)
|
||||
plt->target = (u64)&dummy_tramp;
|
||||
}
|
||||
|
||||
static void build_epilogue(struct jit_ctx *ctx)
|
||||
/* Clobbers BPF registers 1-4, aka x0-x3 */
|
||||
static void __maybe_unused build_bhb_mitigation(struct jit_ctx *ctx)
|
||||
{
|
||||
const u8 r1 = bpf2a64[BPF_REG_1]; /* aka x0 */
|
||||
u8 k = get_spectre_bhb_loop_value();
|
||||
|
||||
if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY) ||
|
||||
cpu_mitigations_off() || __nospectre_bhb ||
|
||||
arm64_get_spectre_v2_state() == SPECTRE_VULNERABLE)
|
||||
return;
|
||||
|
||||
if (capable(CAP_SYS_ADMIN))
|
||||
return;
|
||||
|
||||
if (supports_clearbhb(SCOPE_SYSTEM)) {
|
||||
emit(aarch64_insn_gen_hint(AARCH64_INSN_HINT_CLEARBHB), ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (k) {
|
||||
emit_a64_mov_i64(r1, k, ctx);
|
||||
emit(A64_B(1), ctx);
|
||||
emit(A64_SUBS_I(true, r1, r1, 1), ctx);
|
||||
emit(A64_B_(A64_COND_NE, -2), ctx);
|
||||
emit(aarch64_insn_gen_dsb(AARCH64_INSN_MB_ISH), ctx);
|
||||
emit(aarch64_insn_get_isb_value(), ctx);
|
||||
}
|
||||
|
||||
if (is_spectre_bhb_fw_mitigated()) {
|
||||
emit(A64_ORR_I(false, r1, AARCH64_INSN_REG_ZR,
|
||||
ARM_SMCCC_ARCH_WORKAROUND_3), ctx);
|
||||
switch (arm_smccc_1_1_get_conduit()) {
|
||||
case SMCCC_CONDUIT_HVC:
|
||||
emit(aarch64_insn_get_hvc_value(), ctx);
|
||||
break;
|
||||
case SMCCC_CONDUIT_SMC:
|
||||
emit(aarch64_insn_get_smc_value(), ctx);
|
||||
break;
|
||||
default:
|
||||
pr_err_once("Firmware mitigation enabled with unknown conduit\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void build_epilogue(struct jit_ctx *ctx, bool was_classic)
|
||||
{
|
||||
const u8 r0 = bpf2a64[BPF_REG_0];
|
||||
const u8 ptr = bpf2a64[TCCNT_PTR];
|
||||
@@ -879,10 +925,13 @@ static void build_epilogue(struct jit_ctx *ctx)
|
||||
|
||||
emit(A64_POP(A64_ZR, ptr, A64_SP), ctx);
|
||||
|
||||
if (was_classic)
|
||||
build_bhb_mitigation(ctx);
|
||||
|
||||
/* Restore FP/LR registers */
|
||||
emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
|
||||
|
||||
/* Set return value */
|
||||
/* Move the return value from bpf:r0 (aka x7) to x0 */
|
||||
emit(A64_MOV(1, A64_R(0), r0), ctx);
|
||||
|
||||
/* Authenticate lr */
|
||||
@@ -1826,7 +1875,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
}
|
||||
|
||||
ctx.epilogue_offset = ctx.idx;
|
||||
build_epilogue(&ctx);
|
||||
build_epilogue(&ctx, was_classic);
|
||||
build_plt(&ctx);
|
||||
|
||||
extable_align = __alignof__(struct exception_table_entry);
|
||||
@@ -1889,7 +1938,7 @@ skip_init_ctx:
|
||||
goto out_free_hdr;
|
||||
}
|
||||
|
||||
build_epilogue(&ctx);
|
||||
build_epilogue(&ctx, was_classic);
|
||||
build_plt(&ctx);
|
||||
|
||||
/* Extra pass to validate JITed code. */
|
||||
|
||||
@@ -198,47 +198,57 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re
|
||||
DO_ERROR_INFO(do_trap_load_fault,
|
||||
SIGSEGV, SEGV_ACCERR, "load access fault");
|
||||
|
||||
asmlinkage __visible __trap_section void do_trap_load_misaligned(struct pt_regs *regs)
|
||||
enum misaligned_access_type {
|
||||
MISALIGNED_STORE,
|
||||
MISALIGNED_LOAD,
|
||||
};
|
||||
static const struct {
|
||||
const char *type_str;
|
||||
int (*handler)(struct pt_regs *regs);
|
||||
} misaligned_handler[] = {
|
||||
[MISALIGNED_STORE] = {
|
||||
.type_str = "Oops - store (or AMO) address misaligned",
|
||||
.handler = handle_misaligned_store,
|
||||
},
|
||||
[MISALIGNED_LOAD] = {
|
||||
.type_str = "Oops - load address misaligned",
|
||||
.handler = handle_misaligned_load,
|
||||
},
|
||||
};
|
||||
|
||||
static void do_trap_misaligned(struct pt_regs *regs, enum misaligned_access_type type)
|
||||
{
|
||||
irqentry_state_t state;
|
||||
|
||||
if (user_mode(regs)) {
|
||||
irqentry_enter_from_user_mode(regs);
|
||||
local_irq_enable();
|
||||
} else {
|
||||
state = irqentry_nmi_enter(regs);
|
||||
}
|
||||
|
||||
if (handle_misaligned_load(regs))
|
||||
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
||||
"Oops - load address misaligned");
|
||||
if (misaligned_handler[type].handler(regs))
|
||||
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
||||
misaligned_handler[type].type_str);
|
||||
|
||||
if (user_mode(regs)) {
|
||||
local_irq_disable();
|
||||
irqentry_exit_to_user_mode(regs);
|
||||
} else {
|
||||
irqentry_state_t state = irqentry_nmi_enter(regs);
|
||||
|
||||
if (handle_misaligned_load(regs))
|
||||
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
||||
"Oops - load address misaligned");
|
||||
|
||||
irqentry_nmi_exit(regs, state);
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage __visible __trap_section void do_trap_load_misaligned(struct pt_regs *regs)
|
||||
{
|
||||
do_trap_misaligned(regs, MISALIGNED_LOAD);
|
||||
}
|
||||
|
||||
asmlinkage __visible __trap_section void do_trap_store_misaligned(struct pt_regs *regs)
|
||||
{
|
||||
if (user_mode(regs)) {
|
||||
irqentry_enter_from_user_mode(regs);
|
||||
|
||||
if (handle_misaligned_store(regs))
|
||||
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
||||
"Oops - store (or AMO) address misaligned");
|
||||
|
||||
irqentry_exit_to_user_mode(regs);
|
||||
} else {
|
||||
irqentry_state_t state = irqentry_nmi_enter(regs);
|
||||
|
||||
if (handle_misaligned_store(regs))
|
||||
do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc,
|
||||
"Oops - store (or AMO) address misaligned");
|
||||
|
||||
irqentry_nmi_exit(regs, state);
|
||||
}
|
||||
do_trap_misaligned(regs, MISALIGNED_STORE);
|
||||
}
|
||||
|
||||
DO_ERROR_INFO(do_trap_store_fault,
|
||||
SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
|
||||
DO_ERROR_INFO(do_trap_ecall_s,
|
||||
|
||||
@@ -83,8 +83,6 @@ extern void time_travel_not_configured(void);
|
||||
#define time_travel_del_event(...) time_travel_not_configured()
|
||||
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
|
||||
|
||||
extern unsigned long tt_extra_sched_jiffies;
|
||||
|
||||
/*
|
||||
* Without CONFIG_UML_TIME_TRAVEL_SUPPORT this is a linker error if used,
|
||||
* which is intentional since we really shouldn't link it in that case.
|
||||
|
||||
@@ -31,17 +31,6 @@ void handle_syscall(struct uml_pt_regs *r)
|
||||
goto out;
|
||||
|
||||
syscall = UPT_SYSCALL_NR(r);
|
||||
|
||||
/*
|
||||
* If no time passes, then sched_yield may not actually yield, causing
|
||||
* broken spinlock implementations in userspace (ASAN) to hang for long
|
||||
* periods of time.
|
||||
*/
|
||||
if ((time_travel_mode == TT_MODE_INFCPU ||
|
||||
time_travel_mode == TT_MODE_EXTERNAL) &&
|
||||
syscall == __NR_sched_yield)
|
||||
tt_extra_sched_jiffies += 1;
|
||||
|
||||
if (syscall >= 0 && syscall < __NR_syscalls) {
|
||||
unsigned long ret = EXECUTE_SYSCALL(syscall, regs);
|
||||
|
||||
|
||||
@@ -2748,6 +2748,18 @@ config MITIGATION_SSB
|
||||
of speculative execution in a similar way to the Meltdown and Spectre
|
||||
security vulnerabilities.
|
||||
|
||||
config MITIGATION_ITS
|
||||
bool "Enable Indirect Target Selection mitigation"
|
||||
depends on CPU_SUP_INTEL && X86_64
|
||||
depends on MITIGATION_RETPOLINE && MITIGATION_RETHUNK
|
||||
select EXECMEM
|
||||
default y
|
||||
help
|
||||
Enable Indirect Target Selection (ITS) mitigation. ITS is a bug in
|
||||
BPU on some Intel CPUs that may allow Spectre V2 style attacks. If
|
||||
disabled, mitigation cannot be enabled via cmdline.
|
||||
See <file:Documentation/admin-guide/hw-vuln/indirect-target-selection.rst>
|
||||
|
||||
endif
|
||||
|
||||
config ARCH_HAS_ADD_PAGES
|
||||
|
||||
@@ -1524,7 +1524,9 @@ SYM_CODE_END(rewind_stack_and_make_dead)
|
||||
* ORC to unwind properly.
|
||||
*
|
||||
* The alignment is for performance and not for safety, and may be safely
|
||||
* refactored in the future if needed.
|
||||
* refactored in the future if needed. The .skips are for safety, to ensure
|
||||
* that all RETs are in the second half of a cacheline to mitigate Indirect
|
||||
* Target Selection, rather than taking the slowpath via its_return_thunk.
|
||||
*/
|
||||
SYM_FUNC_START(clear_bhb_loop)
|
||||
push %rbp
|
||||
@@ -1534,10 +1536,22 @@ SYM_FUNC_START(clear_bhb_loop)
|
||||
call 1f
|
||||
jmp 5f
|
||||
.align 64, 0xcc
|
||||
/*
|
||||
* Shift instructions so that the RET is in the upper half of the
|
||||
* cacheline and don't take the slowpath to its_return_thunk.
|
||||
*/
|
||||
.skip 32 - (.Lret1 - 1f), 0xcc
|
||||
ANNOTATE_INTRA_FUNCTION_CALL
|
||||
1: call 2f
|
||||
RET
|
||||
.Lret1: RET
|
||||
.align 64, 0xcc
|
||||
/*
|
||||
* As above shift instructions for RET at .Lret2 as well.
|
||||
*
|
||||
* This should be ideally be: .skip 32 - (.Lret2 - 2f), 0xcc
|
||||
* but some Clang versions (e.g. 18) don't like this.
|
||||
*/
|
||||
.skip 32 - 18, 0xcc
|
||||
2: movl $5, %eax
|
||||
3: jmp 4f
|
||||
nop
|
||||
@@ -1545,7 +1559,7 @@ SYM_FUNC_START(clear_bhb_loop)
|
||||
jnz 3b
|
||||
sub $1, %ecx
|
||||
jnz 1b
|
||||
RET
|
||||
.Lret2: RET
|
||||
5: lfence
|
||||
pop %rbp
|
||||
RET
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/asm.h>
|
||||
#include <asm/bug.h>
|
||||
|
||||
#define ALT_FLAGS_SHIFT 16
|
||||
|
||||
@@ -134,6 +135,37 @@ static __always_inline int x86_call_depth_emit_accounting(u8 **pprog,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
extern void its_init_mod(struct module *mod);
|
||||
extern void its_fini_mod(struct module *mod);
|
||||
extern void its_free_mod(struct module *mod);
|
||||
extern u8 *its_static_thunk(int reg);
|
||||
#else /* CONFIG_MITIGATION_ITS */
|
||||
static inline void its_init_mod(struct module *mod) { }
|
||||
static inline void its_fini_mod(struct module *mod) { }
|
||||
static inline void its_free_mod(struct module *mod) { }
|
||||
static inline u8 *its_static_thunk(int reg)
|
||||
{
|
||||
WARN_ONCE(1, "ITS not compiled in");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MITIGATION_RETHUNK) && defined(CONFIG_OBJTOOL)
|
||||
extern bool cpu_wants_rethunk(void);
|
||||
extern bool cpu_wants_rethunk_at(void *addr);
|
||||
#else
|
||||
static __always_inline bool cpu_wants_rethunk(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static __always_inline bool cpu_wants_rethunk_at(void *addr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
extern void alternatives_smp_module_add(struct module *mod, char *name,
|
||||
void *locks, void *locks_end,
|
||||
|
||||
@@ -475,6 +475,7 @@
|
||||
#define X86_FEATURE_CLEAR_BHB_HW (21*32+ 3) /* BHI_DIS_S HW control enabled */
|
||||
#define X86_FEATURE_CLEAR_BHB_LOOP_ON_VMEXIT (21*32+ 4) /* Clear branch history at vmexit using SW loop */
|
||||
#define X86_FEATURE_FAST_CPPC (21*32 + 5) /* AMD Fast CPPC */
|
||||
#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32 + 6) /* Use thunk for indirect branches in lower half of cacheline */
|
||||
|
||||
/*
|
||||
* BUG word(s)
|
||||
@@ -526,4 +527,6 @@
|
||||
#define X86_BUG_RFDS X86_BUG(1*32 + 2) /* "rfds" CPU is vulnerable to Register File Data Sampling */
|
||||
#define X86_BUG_BHI X86_BUG(1*32 + 3) /* "bhi" CPU is affected by Branch History Injection */
|
||||
#define X86_BUG_IBPB_NO_RET X86_BUG(1*32 + 4) /* "ibpb_no_ret" IBPB omits return target predictions */
|
||||
#define X86_BUG_ITS X86_BUG(1*32 + 5) /* "its" CPU is affected by Indirect Target Selection */
|
||||
#define X86_BUG_ITS_NATIVE_ONLY X86_BUG(1*32 + 6) /* "its_native_only" CPU is affected by ITS, VMX is not affected */
|
||||
#endif /* _ASM_X86_CPUFEATURES_H */
|
||||
|
||||
@@ -209,6 +209,14 @@
|
||||
* VERW clears CPU Register
|
||||
* File.
|
||||
*/
|
||||
#define ARCH_CAP_ITS_NO BIT_ULL(62) /*
|
||||
* Not susceptible to
|
||||
* Indirect Target Selection.
|
||||
* This bit is not set by
|
||||
* HW, but is synthesized by
|
||||
* VMMs for guests to know
|
||||
* their affected status.
|
||||
*/
|
||||
|
||||
#define MSR_IA32_FLUSH_CMD 0x0000010b
|
||||
#define L1D_FLUSH BIT(0) /*
|
||||
|
||||
@@ -210,9 +210,8 @@
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Equivalent to -mindirect-branch-cs-prefix; emit the 5 byte jmp/call
|
||||
* to the retpoline thunk with a CS prefix when the register requires
|
||||
* a RAX prefix byte to encode. Also see apply_retpolines().
|
||||
* Emits a conditional CS prefix that is compatible with
|
||||
* -mindirect-branch-cs-prefix.
|
||||
*/
|
||||
.macro __CS_PREFIX reg:req
|
||||
.irp rs,r8,r9,r10,r11,r12,r13,r14,r15
|
||||
@@ -356,10 +355,14 @@
|
||||
".long 999b\n\t" \
|
||||
".popsection\n\t"
|
||||
|
||||
#define ITS_THUNK_SIZE 64
|
||||
|
||||
typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE];
|
||||
typedef u8 its_thunk_t[ITS_THUNK_SIZE];
|
||||
extern retpoline_thunk_t __x86_indirect_thunk_array[];
|
||||
extern retpoline_thunk_t __x86_indirect_call_thunk_array[];
|
||||
extern retpoline_thunk_t __x86_indirect_jump_thunk_array[];
|
||||
extern its_thunk_t __x86_indirect_its_thunk_array[];
|
||||
|
||||
#ifdef CONFIG_MITIGATION_RETHUNK
|
||||
extern void __x86_return_thunk(void);
|
||||
@@ -383,6 +386,12 @@ static inline void srso_return_thunk(void) {}
|
||||
static inline void srso_alias_return_thunk(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
extern void its_return_thunk(void);
|
||||
#else
|
||||
static inline void its_return_thunk(void) {}
|
||||
#endif
|
||||
|
||||
extern void retbleed_return_thunk(void);
|
||||
extern void srso_return_thunk(void);
|
||||
extern void srso_alias_return_thunk(void);
|
||||
@@ -438,20 +447,23 @@ static inline void call_depth_return_thunk(void) {}
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
|
||||
/*
|
||||
* Emits a conditional CS prefix that is compatible with
|
||||
* -mindirect-branch-cs-prefix.
|
||||
*/
|
||||
#define __CS_PREFIX(reg) \
|
||||
".irp rs,r8,r9,r10,r11,r12,r13,r14,r15\n" \
|
||||
".ifc \\rs," reg "\n" \
|
||||
".byte 0x2e\n" \
|
||||
".endif\n" \
|
||||
".endr\n"
|
||||
|
||||
/*
|
||||
* Inline asm uses the %V modifier which is only in newer GCC
|
||||
* which is ensured when CONFIG_MITIGATION_RETPOLINE is defined.
|
||||
*/
|
||||
# define CALL_NOSPEC \
|
||||
ALTERNATIVE_2( \
|
||||
ANNOTATE_RETPOLINE_SAFE \
|
||||
"call *%[thunk_target]\n", \
|
||||
"call __x86_indirect_thunk_%V[thunk_target]\n", \
|
||||
X86_FEATURE_RETPOLINE, \
|
||||
"lfence;\n" \
|
||||
ANNOTATE_RETPOLINE_SAFE \
|
||||
"call *%[thunk_target]\n", \
|
||||
X86_FEATURE_RETPOLINE_LFENCE)
|
||||
#define CALL_NOSPEC __CS_PREFIX("%V[thunk_target]") \
|
||||
"call __x86_indirect_thunk_%V[thunk_target]\n"
|
||||
|
||||
# define THUNK_TARGET(addr) [thunk_target] "r" (addr)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/mmu_context.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/sync_core.h>
|
||||
#include <linux/execmem.h>
|
||||
#include <asm/text-patching.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -32,6 +33,8 @@
|
||||
#include <asm/paravirt.h>
|
||||
#include <asm/asm-prototypes.h>
|
||||
#include <asm/cfi.h>
|
||||
#include <asm/ibt.h>
|
||||
#include <asm/set_memory.h>
|
||||
|
||||
int __read_mostly alternatives_patched;
|
||||
|
||||
@@ -125,6 +128,136 @@ const unsigned char * const x86_nops[ASM_NOP_MAX+1] =
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
static struct module *its_mod;
|
||||
#endif
|
||||
static void *its_page;
|
||||
static unsigned int its_offset;
|
||||
|
||||
/* Initialize a thunk with the "jmp *reg; int3" instructions. */
|
||||
static void *its_init_thunk(void *thunk, int reg)
|
||||
{
|
||||
u8 *bytes = thunk;
|
||||
int i = 0;
|
||||
|
||||
if (reg >= 8) {
|
||||
bytes[i++] = 0x41; /* REX.B prefix */
|
||||
reg -= 8;
|
||||
}
|
||||
bytes[i++] = 0xff;
|
||||
bytes[i++] = 0xe0 + reg; /* jmp *reg */
|
||||
bytes[i++] = 0xcc;
|
||||
|
||||
return thunk;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
void its_init_mod(struct module *mod)
|
||||
{
|
||||
if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS))
|
||||
return;
|
||||
|
||||
mutex_lock(&text_mutex);
|
||||
its_mod = mod;
|
||||
its_page = NULL;
|
||||
}
|
||||
|
||||
void its_fini_mod(struct module *mod)
|
||||
{
|
||||
if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS))
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(its_mod != mod);
|
||||
|
||||
its_mod = NULL;
|
||||
its_page = NULL;
|
||||
mutex_unlock(&text_mutex);
|
||||
|
||||
for (int i = 0; i < mod->its_num_pages; i++) {
|
||||
void *page = mod->its_page_array[i];
|
||||
set_memory_rox((unsigned long)page, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void its_free_mod(struct module *mod)
|
||||
{
|
||||
if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < mod->its_num_pages; i++) {
|
||||
void *page = mod->its_page_array[i];
|
||||
execmem_free(page);
|
||||
}
|
||||
kfree(mod->its_page_array);
|
||||
}
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
static void *its_alloc(void)
|
||||
{
|
||||
void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE);
|
||||
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
if (its_mod) {
|
||||
void *tmp = krealloc(its_mod->its_page_array,
|
||||
(its_mod->its_num_pages+1) * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
|
||||
its_mod->its_page_array = tmp;
|
||||
its_mod->its_page_array[its_mod->its_num_pages++] = page;
|
||||
}
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
return no_free_ptr(page);
|
||||
}
|
||||
|
||||
static void *its_allocate_thunk(int reg)
|
||||
{
|
||||
int size = 3 + (reg / 8);
|
||||
void *thunk;
|
||||
|
||||
if (!its_page || (its_offset + size - 1) >= PAGE_SIZE) {
|
||||
its_page = its_alloc();
|
||||
if (!its_page) {
|
||||
pr_err("ITS page allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(its_page, INT3_INSN_OPCODE, PAGE_SIZE);
|
||||
its_offset = 32;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the indirect branch instruction will be in the lower half
|
||||
* of a cacheline, then update the offset to reach the upper half.
|
||||
*/
|
||||
if ((its_offset + size - 1) % 64 < 32)
|
||||
its_offset = ((its_offset - 1) | 0x3F) + 33;
|
||||
|
||||
thunk = its_page + its_offset;
|
||||
its_offset += size;
|
||||
|
||||
set_memory_rw((unsigned long)its_page, 1);
|
||||
thunk = its_init_thunk(thunk, reg);
|
||||
set_memory_rox((unsigned long)its_page, 1);
|
||||
|
||||
return thunk;
|
||||
}
|
||||
|
||||
u8 *its_static_thunk(int reg)
|
||||
{
|
||||
u8 *thunk = __x86_indirect_its_thunk_array[reg];
|
||||
|
||||
return thunk;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Nomenclature for variable names to simplify and clarify this code and ease
|
||||
* any potential staring at it:
|
||||
@@ -582,7 +715,8 @@ static int emit_indirect(int op, int reg, u8 *bytes)
|
||||
return i;
|
||||
}
|
||||
|
||||
static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes)
|
||||
static int __emit_trampoline(void *addr, struct insn *insn, u8 *bytes,
|
||||
void *call_dest, void *jmp_dest)
|
||||
{
|
||||
u8 op = insn->opcode.bytes[0];
|
||||
int i = 0;
|
||||
@@ -603,7 +737,7 @@ static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8
|
||||
switch (op) {
|
||||
case CALL_INSN_OPCODE:
|
||||
__text_gen_insn(bytes+i, op, addr+i,
|
||||
__x86_indirect_call_thunk_array[reg],
|
||||
call_dest,
|
||||
CALL_INSN_SIZE);
|
||||
i += CALL_INSN_SIZE;
|
||||
break;
|
||||
@@ -611,7 +745,7 @@ static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8
|
||||
case JMP32_INSN_OPCODE:
|
||||
clang_jcc:
|
||||
__text_gen_insn(bytes+i, op, addr+i,
|
||||
__x86_indirect_jump_thunk_array[reg],
|
||||
jmp_dest,
|
||||
JMP32_INSN_SIZE);
|
||||
i += JMP32_INSN_SIZE;
|
||||
break;
|
||||
@@ -626,6 +760,39 @@ clang_jcc:
|
||||
return i;
|
||||
}
|
||||
|
||||
static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes)
|
||||
{
|
||||
return __emit_trampoline(addr, insn, bytes,
|
||||
__x86_indirect_call_thunk_array[reg],
|
||||
__x86_indirect_jump_thunk_array[reg]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
static int emit_its_trampoline(void *addr, struct insn *insn, int reg, u8 *bytes)
|
||||
{
|
||||
u8 *thunk = __x86_indirect_its_thunk_array[reg];
|
||||
u8 *tmp = its_allocate_thunk(reg);
|
||||
|
||||
if (tmp)
|
||||
thunk = tmp;
|
||||
|
||||
return __emit_trampoline(addr, insn, bytes, thunk, thunk);
|
||||
}
|
||||
|
||||
/* Check if an indirect branch is at ITS-unsafe address */
|
||||
static bool cpu_wants_indirect_its_thunk_at(unsigned long addr, int reg)
|
||||
{
|
||||
if (!cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS))
|
||||
return false;
|
||||
|
||||
/* Indirect branch opcode is 2 or 3 bytes depending on reg */
|
||||
addr += 1 + reg / 8;
|
||||
|
||||
/* Lower-half of the cacheline? */
|
||||
return !(addr & 0x20);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Rewrite the compiler generated retpoline thunk calls.
|
||||
*
|
||||
@@ -700,6 +867,15 @@ static int patch_retpoline(void *addr, struct insn *insn, u8 *bytes)
|
||||
bytes[i++] = 0xe8; /* LFENCE */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
/*
|
||||
* Check if the address of last byte of emitted-indirect is in
|
||||
* lower-half of the cacheline. Such branches need ITS mitigation.
|
||||
*/
|
||||
if (cpu_wants_indirect_its_thunk_at((unsigned long)addr + i, reg))
|
||||
return emit_its_trampoline(addr, insn, reg, bytes);
|
||||
#endif
|
||||
|
||||
ret = emit_indirect(op, reg, bytes + i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -771,6 +947,21 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
|
||||
|
||||
#ifdef CONFIG_MITIGATION_RETHUNK
|
||||
|
||||
bool cpu_wants_rethunk(void)
|
||||
{
|
||||
return cpu_feature_enabled(X86_FEATURE_RETHUNK);
|
||||
}
|
||||
|
||||
bool cpu_wants_rethunk_at(void *addr)
|
||||
{
|
||||
if (!cpu_feature_enabled(X86_FEATURE_RETHUNK))
|
||||
return false;
|
||||
if (x86_return_thunk != its_return_thunk)
|
||||
return true;
|
||||
|
||||
return !((unsigned long)addr & 0x20);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rewrite the compiler generated return thunk tail-calls.
|
||||
*
|
||||
@@ -787,7 +978,7 @@ static int patch_return(void *addr, struct insn *insn, u8 *bytes)
|
||||
int i = 0;
|
||||
|
||||
/* Patch the custom return thunks... */
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) {
|
||||
if (cpu_wants_rethunk_at(addr)) {
|
||||
i = JMP32_INSN_SIZE;
|
||||
__text_gen_insn(bytes, JMP32_INSN_OPCODE, addr, x86_return_thunk, i);
|
||||
} else {
|
||||
@@ -804,7 +995,7 @@ void __init_or_module noinline apply_returns(s32 *start, s32 *end)
|
||||
{
|
||||
s32 *s;
|
||||
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
|
||||
if (cpu_wants_rethunk())
|
||||
static_call_force_reinit();
|
||||
|
||||
for (s = start; s < end; s++) {
|
||||
@@ -1636,6 +1827,8 @@ static noinline void __init alt_reloc_selftest(void)
|
||||
|
||||
void __init alternative_instructions(void)
|
||||
{
|
||||
u64 ibt;
|
||||
|
||||
int3_selftest();
|
||||
|
||||
/*
|
||||
@@ -1662,6 +1855,9 @@ void __init alternative_instructions(void)
|
||||
*/
|
||||
paravirt_set_cap();
|
||||
|
||||
/* Keep CET-IBT disabled until caller/callee are patched */
|
||||
ibt = ibt_save(/*disable*/ true);
|
||||
|
||||
__apply_fineibt(__retpoline_sites, __retpoline_sites_end,
|
||||
__cfi_sites, __cfi_sites_end, true);
|
||||
|
||||
@@ -1685,6 +1881,8 @@ void __init alternative_instructions(void)
|
||||
*/
|
||||
apply_seal_endbr(__ibt_endbr_seal, __ibt_endbr_seal_end);
|
||||
|
||||
ibt_restore(ibt);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Patch to UP if other cpus not imminent. */
|
||||
if (!noreplace_smp && (num_present_cpus() == 1 || setup_max_cpus <= 1)) {
|
||||
|
||||
@@ -49,6 +49,7 @@ static void __init srbds_select_mitigation(void);
|
||||
static void __init l1d_flush_select_mitigation(void);
|
||||
static void __init srso_select_mitigation(void);
|
||||
static void __init gds_select_mitigation(void);
|
||||
static void __init its_select_mitigation(void);
|
||||
|
||||
/* The base value of the SPEC_CTRL MSR without task-specific bits set */
|
||||
u64 x86_spec_ctrl_base;
|
||||
@@ -67,6 +68,14 @@ static DEFINE_MUTEX(spec_ctrl_mutex);
|
||||
|
||||
void (*x86_return_thunk)(void) __ro_after_init = __x86_return_thunk;
|
||||
|
||||
static void __init set_return_thunk(void *thunk)
|
||||
{
|
||||
if (x86_return_thunk != __x86_return_thunk)
|
||||
pr_warn("x86/bugs: return thunk changed\n");
|
||||
|
||||
x86_return_thunk = thunk;
|
||||
}
|
||||
|
||||
/* Update SPEC_CTRL MSR and its cached copy unconditionally */
|
||||
static void update_spec_ctrl(u64 val)
|
||||
{
|
||||
@@ -175,6 +184,7 @@ void __init cpu_select_mitigations(void)
|
||||
*/
|
||||
srso_select_mitigation();
|
||||
gds_select_mitigation();
|
||||
its_select_mitigation();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1104,7 +1114,7 @@ do_cmd_auto:
|
||||
setup_force_cpu_cap(X86_FEATURE_RETHUNK);
|
||||
setup_force_cpu_cap(X86_FEATURE_UNRET);
|
||||
|
||||
x86_return_thunk = retbleed_return_thunk;
|
||||
set_return_thunk(retbleed_return_thunk);
|
||||
|
||||
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
|
||||
boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
|
||||
@@ -1139,7 +1149,7 @@ do_cmd_auto:
|
||||
setup_force_cpu_cap(X86_FEATURE_RETHUNK);
|
||||
setup_force_cpu_cap(X86_FEATURE_CALL_DEPTH);
|
||||
|
||||
x86_return_thunk = call_depth_return_thunk;
|
||||
set_return_thunk(call_depth_return_thunk);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1173,6 +1183,145 @@ do_cmd_auto:
|
||||
pr_info("%s\n", retbleed_strings[retbleed_mitigation]);
|
||||
}
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "ITS: " fmt
|
||||
|
||||
enum its_mitigation_cmd {
|
||||
ITS_CMD_OFF,
|
||||
ITS_CMD_ON,
|
||||
ITS_CMD_VMEXIT,
|
||||
ITS_CMD_RSB_STUFF,
|
||||
};
|
||||
|
||||
enum its_mitigation {
|
||||
ITS_MITIGATION_OFF,
|
||||
ITS_MITIGATION_VMEXIT_ONLY,
|
||||
ITS_MITIGATION_ALIGNED_THUNKS,
|
||||
ITS_MITIGATION_RETPOLINE_STUFF,
|
||||
};
|
||||
|
||||
static const char * const its_strings[] = {
|
||||
[ITS_MITIGATION_OFF] = "Vulnerable",
|
||||
[ITS_MITIGATION_VMEXIT_ONLY] = "Mitigation: Vulnerable, KVM: Not affected",
|
||||
[ITS_MITIGATION_ALIGNED_THUNKS] = "Mitigation: Aligned branch/return thunks",
|
||||
[ITS_MITIGATION_RETPOLINE_STUFF] = "Mitigation: Retpolines, Stuffing RSB",
|
||||
};
|
||||
|
||||
static enum its_mitigation its_mitigation __ro_after_init = ITS_MITIGATION_ALIGNED_THUNKS;
|
||||
|
||||
static enum its_mitigation_cmd its_cmd __ro_after_init =
|
||||
IS_ENABLED(CONFIG_MITIGATION_ITS) ? ITS_CMD_ON : ITS_CMD_OFF;
|
||||
|
||||
static int __init its_parse_cmdline(char *str)
|
||||
{
|
||||
if (!str)
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_MITIGATION_ITS)) {
|
||||
pr_err("Mitigation disabled at compile time, ignoring option (%s)", str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(str, "off")) {
|
||||
its_cmd = ITS_CMD_OFF;
|
||||
} else if (!strcmp(str, "on")) {
|
||||
its_cmd = ITS_CMD_ON;
|
||||
} else if (!strcmp(str, "force")) {
|
||||
its_cmd = ITS_CMD_ON;
|
||||
setup_force_cpu_bug(X86_BUG_ITS);
|
||||
} else if (!strcmp(str, "vmexit")) {
|
||||
its_cmd = ITS_CMD_VMEXIT;
|
||||
} else if (!strcmp(str, "stuff")) {
|
||||
its_cmd = ITS_CMD_RSB_STUFF;
|
||||
} else {
|
||||
pr_err("Ignoring unknown indirect_target_selection option (%s).", str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("indirect_target_selection", its_parse_cmdline);
|
||||
|
||||
static void __init its_select_mitigation(void)
|
||||
{
|
||||
enum its_mitigation_cmd cmd = its_cmd;
|
||||
|
||||
if (!boot_cpu_has_bug(X86_BUG_ITS) || cpu_mitigations_off()) {
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Retpoline+CDT mitigates ITS, bail out */
|
||||
if (boot_cpu_has(X86_FEATURE_RETPOLINE) &&
|
||||
boot_cpu_has(X86_FEATURE_CALL_DEPTH)) {
|
||||
its_mitigation = ITS_MITIGATION_RETPOLINE_STUFF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Exit early to avoid irrelevant warnings */
|
||||
if (cmd == ITS_CMD_OFF) {
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
goto out;
|
||||
}
|
||||
if (spectre_v2_enabled == SPECTRE_V2_NONE) {
|
||||
pr_err("WARNING: Spectre-v2 mitigation is off, disabling ITS\n");
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
goto out;
|
||||
}
|
||||
if (!IS_ENABLED(CONFIG_MITIGATION_RETPOLINE) ||
|
||||
!IS_ENABLED(CONFIG_MITIGATION_RETHUNK)) {
|
||||
pr_err("WARNING: ITS mitigation depends on retpoline and rethunk support\n");
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
goto out;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)) {
|
||||
pr_err("WARNING: ITS mitigation is not compatible with CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B\n");
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
goto out;
|
||||
}
|
||||
if (boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE)) {
|
||||
pr_err("WARNING: ITS mitigation is not compatible with lfence mitigation\n");
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cmd == ITS_CMD_RSB_STUFF &&
|
||||
(!boot_cpu_has(X86_FEATURE_RETPOLINE) || !IS_ENABLED(CONFIG_MITIGATION_CALL_DEPTH_TRACKING))) {
|
||||
pr_err("RSB stuff mitigation not supported, using default\n");
|
||||
cmd = ITS_CMD_ON;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case ITS_CMD_OFF:
|
||||
its_mitigation = ITS_MITIGATION_OFF;
|
||||
break;
|
||||
case ITS_CMD_VMEXIT:
|
||||
if (boot_cpu_has_bug(X86_BUG_ITS_NATIVE_ONLY)) {
|
||||
its_mitigation = ITS_MITIGATION_VMEXIT_ONLY;
|
||||
goto out;
|
||||
}
|
||||
fallthrough;
|
||||
case ITS_CMD_ON:
|
||||
its_mitigation = ITS_MITIGATION_ALIGNED_THUNKS;
|
||||
if (!boot_cpu_has(X86_FEATURE_RETPOLINE))
|
||||
setup_force_cpu_cap(X86_FEATURE_INDIRECT_THUNK_ITS);
|
||||
setup_force_cpu_cap(X86_FEATURE_RETHUNK);
|
||||
set_return_thunk(its_return_thunk);
|
||||
break;
|
||||
case ITS_CMD_RSB_STUFF:
|
||||
its_mitigation = ITS_MITIGATION_RETPOLINE_STUFF;
|
||||
setup_force_cpu_cap(X86_FEATURE_RETHUNK);
|
||||
setup_force_cpu_cap(X86_FEATURE_CALL_DEPTH);
|
||||
set_return_thunk(call_depth_return_thunk);
|
||||
if (retbleed_mitigation == RETBLEED_MITIGATION_NONE) {
|
||||
retbleed_mitigation = RETBLEED_MITIGATION_STUFF;
|
||||
pr_info("Retbleed mitigation updated to stuffing\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
out:
|
||||
pr_info("%s\n", its_strings[its_mitigation]);
|
||||
}
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "Spectre V2 : " fmt
|
||||
|
||||
@@ -1684,11 +1833,11 @@ static void __init bhi_select_mitigation(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mitigate in hardware if supported */
|
||||
if (spec_ctrl_bhi_dis())
|
||||
if (!IS_ENABLED(CONFIG_X86_64))
|
||||
return;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_X86_64))
|
||||
/* Mitigate in hardware if supported */
|
||||
if (spec_ctrl_bhi_dis())
|
||||
return;
|
||||
|
||||
if (bhi_mitigation == BHI_MITIGATION_VMEXIT_ONLY) {
|
||||
@@ -2624,10 +2773,10 @@ static void __init srso_select_mitigation(void)
|
||||
|
||||
if (boot_cpu_data.x86 == 0x19) {
|
||||
setup_force_cpu_cap(X86_FEATURE_SRSO_ALIAS);
|
||||
x86_return_thunk = srso_alias_return_thunk;
|
||||
set_return_thunk(srso_alias_return_thunk);
|
||||
} else {
|
||||
setup_force_cpu_cap(X86_FEATURE_SRSO);
|
||||
x86_return_thunk = srso_return_thunk;
|
||||
set_return_thunk(srso_return_thunk);
|
||||
}
|
||||
if (has_microcode)
|
||||
srso_mitigation = SRSO_MITIGATION_SAFE_RET;
|
||||
@@ -2802,6 +2951,11 @@ static ssize_t rfds_show_state(char *buf)
|
||||
return sysfs_emit(buf, "%s\n", rfds_strings[rfds_mitigation]);
|
||||
}
|
||||
|
||||
static ssize_t its_show_state(char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%s\n", its_strings[its_mitigation]);
|
||||
}
|
||||
|
||||
static char *stibp_state(void)
|
||||
{
|
||||
if (spectre_v2_in_eibrs_mode(spectre_v2_enabled) &&
|
||||
@@ -2984,6 +3138,9 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
|
||||
case X86_BUG_RFDS:
|
||||
return rfds_show_state(buf);
|
||||
|
||||
case X86_BUG_ITS:
|
||||
return its_show_state(buf);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -3063,6 +3220,11 @@ ssize_t cpu_show_reg_file_data_sampling(struct device *dev, struct device_attrib
|
||||
{
|
||||
return cpu_show_common(dev, attr, buf, X86_BUG_RFDS);
|
||||
}
|
||||
|
||||
ssize_t cpu_show_indirect_target_selection(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return cpu_show_common(dev, attr, buf, X86_BUG_ITS);
|
||||
}
|
||||
#endif
|
||||
|
||||
void __warn_thunk(void)
|
||||
|
||||
@@ -1228,6 +1228,10 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = {
|
||||
#define GDS BIT(6)
|
||||
/* CPU is affected by Register File Data Sampling */
|
||||
#define RFDS BIT(7)
|
||||
/* CPU is affected by Indirect Target Selection */
|
||||
#define ITS BIT(8)
|
||||
/* CPU is affected by Indirect Target Selection, but guest-host isolation is not affected */
|
||||
#define ITS_NATIVE_ONLY BIT(9)
|
||||
|
||||
static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = {
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_IVYBRIDGE, X86_STEPPING_ANY, SRBDS),
|
||||
@@ -1239,22 +1243,25 @@ static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = {
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_BROADWELL_G, X86_STEPPING_ANY, SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_BROADWELL_X, X86_STEPPING_ANY, MMIO),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_BROADWELL, X86_STEPPING_ANY, SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_SKYLAKE_X, X86_STEPPING_ANY, MMIO | RETBLEED | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_SKYLAKE_X, X86_STEPPINGS(0x0, 0x5), MMIO | RETBLEED | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_SKYLAKE_X, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | ITS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_SKYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_SKYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_KABYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_KABYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_KABYLAKE_L, X86_STEPPINGS(0x0, 0xb), MMIO | RETBLEED | GDS | SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_KABYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS | ITS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_KABYLAKE, X86_STEPPINGS(0x0, 0xc), MMIO | RETBLEED | GDS | SRBDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_KABYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS | ITS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_CANNONLAKE_L, X86_STEPPING_ANY, RETBLEED),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ICELAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ICELAKE_D, X86_STEPPING_ANY, MMIO | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ICELAKE_X, X86_STEPPING_ANY, MMIO | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_COMETLAKE, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_COMETLAKE_L, X86_STEPPINGS(0x0, 0x0), MMIO | RETBLEED),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_COMETLAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_TIGERLAKE_L, X86_STEPPING_ANY, GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_TIGERLAKE, X86_STEPPING_ANY, GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ICELAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS | ITS_NATIVE_ONLY),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ICELAKE_D, X86_STEPPING_ANY, MMIO | GDS | ITS | ITS_NATIVE_ONLY),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ICELAKE_X, X86_STEPPING_ANY, MMIO | GDS | ITS | ITS_NATIVE_ONLY),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_COMETLAKE, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_COMETLAKE_L, X86_STEPPINGS(0x0, 0x0), MMIO | RETBLEED | ITS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_COMETLAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS | ITS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_TIGERLAKE_L, X86_STEPPING_ANY, GDS | ITS | ITS_NATIVE_ONLY),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_TIGERLAKE, X86_STEPPING_ANY, GDS | ITS | ITS_NATIVE_ONLY),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_LAKEFIELD, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ROCKETLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ROCKETLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | ITS | ITS_NATIVE_ONLY),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ALDERLAKE, X86_STEPPING_ANY, RFDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_ALDERLAKE_L, X86_STEPPING_ANY, RFDS),
|
||||
VULNBL_INTEL_STEPPINGS(INTEL_RAPTORLAKE, X86_STEPPING_ANY, RFDS),
|
||||
@@ -1318,6 +1325,32 @@ static bool __init vulnerable_to_rfds(u64 x86_arch_cap_msr)
|
||||
return cpu_matches(cpu_vuln_blacklist, RFDS);
|
||||
}
|
||||
|
||||
static bool __init vulnerable_to_its(u64 x86_arch_cap_msr)
|
||||
{
|
||||
/* The "immunity" bit trumps everything else: */
|
||||
if (x86_arch_cap_msr & ARCH_CAP_ITS_NO)
|
||||
return false;
|
||||
if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
|
||||
return false;
|
||||
|
||||
/* None of the affected CPUs have BHI_CTRL */
|
||||
if (boot_cpu_has(X86_FEATURE_BHI_CTRL))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If a VMM did not expose ITS_NO, assume that a guest could
|
||||
* be running on a vulnerable hardware or may migrate to such
|
||||
* hardware.
|
||||
*/
|
||||
if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
|
||||
return true;
|
||||
|
||||
if (cpu_matches(cpu_vuln_blacklist, ITS))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
|
||||
{
|
||||
u64 x86_arch_cap_msr = x86_read_arch_cap_msr();
|
||||
@@ -1437,9 +1470,12 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
|
||||
if (vulnerable_to_rfds(x86_arch_cap_msr))
|
||||
setup_force_cpu_bug(X86_BUG_RFDS);
|
||||
|
||||
/* When virtualized, eIBRS could be hidden, assume vulnerable */
|
||||
if (!(x86_arch_cap_msr & ARCH_CAP_BHI_NO) &&
|
||||
!cpu_matches(cpu_vuln_whitelist, NO_BHI) &&
|
||||
/*
|
||||
* Intel parts with eIBRS are vulnerable to BHI attacks. Parts with
|
||||
* BHI_NO still need to use the BHI mitigation to prevent Intra-mode
|
||||
* attacks. When virtualized, eIBRS could be hidden, assume vulnerable.
|
||||
*/
|
||||
if (!cpu_matches(cpu_vuln_whitelist, NO_BHI) &&
|
||||
(boot_cpu_has(X86_FEATURE_IBRS_ENHANCED) ||
|
||||
boot_cpu_has(X86_FEATURE_HYPERVISOR)))
|
||||
setup_force_cpu_bug(X86_BUG_BHI);
|
||||
@@ -1447,6 +1483,12 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
|
||||
if (cpu_has(c, X86_FEATURE_AMD_IBPB) && !cpu_has(c, X86_FEATURE_AMD_IBPB_RET))
|
||||
setup_force_cpu_bug(X86_BUG_IBPB_NO_RET);
|
||||
|
||||
if (vulnerable_to_its(x86_arch_cap_msr)) {
|
||||
setup_force_cpu_bug(X86_BUG_ITS);
|
||||
if (cpu_matches(cpu_vuln_blacklist, ITS_NATIVE_ONLY))
|
||||
setup_force_cpu_bug(X86_BUG_ITS_NATIVE_ONLY);
|
||||
}
|
||||
|
||||
if (cpu_matches(cpu_vuln_whitelist, NO_MELTDOWN))
|
||||
return;
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
|
||||
goto fail;
|
||||
|
||||
ip = trampoline + size;
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
|
||||
if (cpu_wants_rethunk_at(ip))
|
||||
__text_gen_insn(ip, JMP32_INSN_OPCODE, ip, x86_return_thunk, JMP32_INSN_SIZE);
|
||||
else
|
||||
memcpy(ip, retq, sizeof(retq));
|
||||
|
||||
@@ -251,6 +251,8 @@ int module_finalize(const Elf_Ehdr *hdr,
|
||||
ibt_endbr = s;
|
||||
}
|
||||
|
||||
its_init_mod(me);
|
||||
|
||||
if (retpolines || cfi) {
|
||||
void *rseg = NULL, *cseg = NULL;
|
||||
unsigned int rsize = 0, csize = 0;
|
||||
@@ -271,6 +273,9 @@ int module_finalize(const Elf_Ehdr *hdr,
|
||||
void *rseg = (void *)retpolines->sh_addr;
|
||||
apply_retpolines(rseg, rseg + retpolines->sh_size);
|
||||
}
|
||||
|
||||
its_fini_mod(me);
|
||||
|
||||
if (returns) {
|
||||
void *rseg = (void *)returns->sh_addr;
|
||||
apply_returns(rseg, rseg + returns->sh_size);
|
||||
@@ -318,4 +323,5 @@ int module_finalize(const Elf_Ehdr *hdr,
|
||||
void module_arch_cleanup(struct module *mod)
|
||||
{
|
||||
alternatives_smp_module_del(mod);
|
||||
its_free_mod(mod);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
|
||||
break;
|
||||
|
||||
case RET:
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
|
||||
if (cpu_wants_rethunk_at(insn))
|
||||
code = text_gen_insn(JMP32_INSN_OPCODE, insn, x86_return_thunk);
|
||||
else
|
||||
code = &retinsn;
|
||||
@@ -90,7 +90,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
|
||||
case JCC:
|
||||
if (!func) {
|
||||
func = __static_call_return;
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
|
||||
if (cpu_wants_rethunk())
|
||||
func = x86_return_thunk;
|
||||
}
|
||||
|
||||
|
||||
@@ -530,4 +530,14 @@ INIT_PER_CPU(irq_stack_backing_store);
|
||||
"SRSO function pair won't alias");
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)
|
||||
. = ASSERT(__x86_indirect_its_thunk_rax & 0x20, "__x86_indirect_thunk_rax not in second half of cacheline");
|
||||
. = ASSERT(((__x86_indirect_its_thunk_rcx - __x86_indirect_its_thunk_rax) % 64) == 0, "Indirect thunks are not cacheline apart");
|
||||
. = ASSERT(__x86_indirect_its_thunk_array == __x86_indirect_its_thunk_rax, "Gap in ITS thunk array");
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MITIGATION_ITS) && !defined(CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B)
|
||||
. = ASSERT(its_return_thunk & 0x20, "its_return_thunk not in second half of cacheline");
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_X86_64 */
|
||||
|
||||
@@ -1623,7 +1623,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_rdpmc);
|
||||
ARCH_CAP_PSCHANGE_MC_NO | ARCH_CAP_TSX_CTRL_MSR | ARCH_CAP_TAA_NO | \
|
||||
ARCH_CAP_SBDR_SSDP_NO | ARCH_CAP_FBSDP_NO | ARCH_CAP_PSDP_NO | \
|
||||
ARCH_CAP_FB_CLEAR | ARCH_CAP_RRSBA | ARCH_CAP_PBRSB_NO | ARCH_CAP_GDS_NO | \
|
||||
ARCH_CAP_RFDS_NO | ARCH_CAP_RFDS_CLEAR | ARCH_CAP_BHI_NO)
|
||||
ARCH_CAP_RFDS_NO | ARCH_CAP_RFDS_CLEAR | ARCH_CAP_BHI_NO | ARCH_CAP_ITS_NO)
|
||||
|
||||
static u64 kvm_get_arch_capabilities(void)
|
||||
{
|
||||
@@ -1657,6 +1657,8 @@ static u64 kvm_get_arch_capabilities(void)
|
||||
data |= ARCH_CAP_MDS_NO;
|
||||
if (!boot_cpu_has_bug(X86_BUG_RFDS))
|
||||
data |= ARCH_CAP_RFDS_NO;
|
||||
if (!boot_cpu_has_bug(X86_BUG_ITS))
|
||||
data |= ARCH_CAP_ITS_NO;
|
||||
|
||||
if (!boot_cpu_has(X86_FEATURE_RTM)) {
|
||||
/*
|
||||
|
||||
@@ -366,6 +366,45 @@ SYM_FUNC_END(call_depth_return_thunk)
|
||||
|
||||
#endif /* CONFIG_MITIGATION_CALL_DEPTH_TRACKING */
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
|
||||
.macro ITS_THUNK reg
|
||||
|
||||
SYM_INNER_LABEL(__x86_indirect_its_thunk_\reg, SYM_L_GLOBAL)
|
||||
UNWIND_HINT_UNDEFINED
|
||||
ANNOTATE_NOENDBR
|
||||
ANNOTATE_RETPOLINE_SAFE
|
||||
jmp *%\reg
|
||||
int3
|
||||
.align 32, 0xcc /* fill to the end of the line */
|
||||
.skip 32, 0xcc /* skip to the next upper half */
|
||||
.endm
|
||||
|
||||
/* ITS mitigation requires thunks be aligned to upper half of cacheline */
|
||||
.align 64, 0xcc
|
||||
.skip 32, 0xcc
|
||||
SYM_CODE_START(__x86_indirect_its_thunk_array)
|
||||
|
||||
#define GEN(reg) ITS_THUNK reg
|
||||
#include <asm/GEN-for-each-reg.h>
|
||||
#undef GEN
|
||||
|
||||
.align 64, 0xcc
|
||||
SYM_CODE_END(__x86_indirect_its_thunk_array)
|
||||
|
||||
.align 64, 0xcc
|
||||
.skip 32, 0xcc
|
||||
SYM_CODE_START(its_return_thunk)
|
||||
UNWIND_HINT_FUNC
|
||||
ANNOTATE_NOENDBR
|
||||
ANNOTATE_UNRET_SAFE
|
||||
ret
|
||||
int3
|
||||
SYM_CODE_END(its_return_thunk)
|
||||
EXPORT_SYMBOL(its_return_thunk)
|
||||
|
||||
#endif /* CONFIG_MITIGATION_ITS */
|
||||
|
||||
/*
|
||||
* This function name is magical and is used by -mfunction-return=thunk-extern
|
||||
* for the compiler to generate JMPs to it.
|
||||
|
||||
@@ -41,6 +41,8 @@ static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
|
||||
#define EMIT2(b1, b2) EMIT((b1) + ((b2) << 8), 2)
|
||||
#define EMIT3(b1, b2, b3) EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3)
|
||||
#define EMIT4(b1, b2, b3, b4) EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4)
|
||||
#define EMIT5(b1, b2, b3, b4, b5) \
|
||||
do { EMIT1(b1); EMIT4(b2, b3, b4, b5); } while (0)
|
||||
|
||||
#define EMIT1_off32(b1, off) \
|
||||
do { EMIT1(b1); EMIT(off, 4); } while (0)
|
||||
@@ -637,7 +639,10 @@ static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
|
||||
if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) {
|
||||
OPTIMIZER_HIDE_VAR(reg);
|
||||
emit_jump(&prog, its_static_thunk(reg), ip);
|
||||
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
|
||||
EMIT_LFENCE();
|
||||
EMIT2(0xFF, 0xE0 + reg);
|
||||
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) {
|
||||
@@ -659,7 +664,7 @@ static void emit_return(u8 **pprog, u8 *ip)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
||||
if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) {
|
||||
if (cpu_wants_rethunk()) {
|
||||
emit_jump(&prog, x86_return_thunk, ip);
|
||||
} else {
|
||||
EMIT1(0xC3); /* ret */
|
||||
@@ -1412,6 +1417,48 @@ static void emit_shiftx(u8 **pprog, u32 dst_reg, u8 src_reg, bool is64, u8 op)
|
||||
#define LOAD_TAIL_CALL_CNT_PTR(stack) \
|
||||
__LOAD_TCC_PTR(BPF_TAIL_CALL_CNT_PTR_STACK_OFF(stack))
|
||||
|
||||
static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
|
||||
struct bpf_prog *bpf_prog)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
u8 *func;
|
||||
|
||||
if (cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_LOOP)) {
|
||||
/* The clearing sequence clobbers eax and ecx. */
|
||||
EMIT1(0x50); /* push rax */
|
||||
EMIT1(0x51); /* push rcx */
|
||||
ip += 2;
|
||||
|
||||
func = (u8 *)clear_bhb_loop;
|
||||
ip += x86_call_depth_emit_accounting(&prog, func, ip);
|
||||
|
||||
if (emit_call(&prog, func, ip))
|
||||
return -EINVAL;
|
||||
EMIT1(0x59); /* pop rcx */
|
||||
EMIT1(0x58); /* pop rax */
|
||||
}
|
||||
/* Insert IBHF instruction */
|
||||
if ((cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_LOOP) &&
|
||||
cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) ||
|
||||
cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_HW)) {
|
||||
/*
|
||||
* Add an Indirect Branch History Fence (IBHF). IBHF acts as a
|
||||
* fence preventing branch history from before the fence from
|
||||
* affecting indirect branches after the fence. This is
|
||||
* specifically used in cBPF jitted code to prevent Intra-mode
|
||||
* BHI attacks. The IBHF instruction is designed to be a NOP on
|
||||
* hardware that doesn't need or support it. The REP and REX.W
|
||||
* prefixes are required by the microcode, and they also ensure
|
||||
* that the NOP is unlikely to be used in existing code.
|
||||
*
|
||||
* IBHF is not a valid instruction in 32-bit mode.
|
||||
*/
|
||||
EMIT5(0xF3, 0x48, 0x0F, 0x1E, 0xF8); /* ibhf */
|
||||
}
|
||||
*pprog = prog;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image,
|
||||
int oldproglen, struct jit_context *ctx, bool jmp_padding)
|
||||
{
|
||||
@@ -2402,6 +2449,13 @@ emit_jmp:
|
||||
seen_exit = true;
|
||||
/* Update cleanup_addr */
|
||||
ctx->cleanup_addr = proglen;
|
||||
if (bpf_prog_was_classic(bpf_prog) &&
|
||||
!capable(CAP_SYS_ADMIN)) {
|
||||
u8 *ip = image + addrs[i - 1];
|
||||
|
||||
if (emit_spectre_bhb_barrier(&prog, ip, bpf_prog))
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bpf_prog->aux->exception_boundary) {
|
||||
pop_callee_regs(&prog, all_callee_regs_used);
|
||||
pop_r12(&prog);
|
||||
|
||||
@@ -599,6 +599,7 @@ CPU_SHOW_VULN_FALLBACK(retbleed);
|
||||
CPU_SHOW_VULN_FALLBACK(spec_rstack_overflow);
|
||||
CPU_SHOW_VULN_FALLBACK(gds);
|
||||
CPU_SHOW_VULN_FALLBACK(reg_file_data_sampling);
|
||||
CPU_SHOW_VULN_FALLBACK(indirect_target_selection);
|
||||
|
||||
static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
|
||||
static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
|
||||
@@ -614,6 +615,7 @@ static DEVICE_ATTR(retbleed, 0444, cpu_show_retbleed, NULL);
|
||||
static DEVICE_ATTR(spec_rstack_overflow, 0444, cpu_show_spec_rstack_overflow, NULL);
|
||||
static DEVICE_ATTR(gather_data_sampling, 0444, cpu_show_gds, NULL);
|
||||
static DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling, NULL);
|
||||
static DEVICE_ATTR(indirect_target_selection, 0444, cpu_show_indirect_target_selection, NULL);
|
||||
|
||||
static struct attribute *cpu_root_vulnerabilities_attrs[] = {
|
||||
&dev_attr_meltdown.attr,
|
||||
@@ -630,6 +632,7 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = {
|
||||
&dev_attr_spec_rstack_overflow.attr,
|
||||
&dev_attr_gather_data_sampling.attr,
|
||||
&dev_attr_reg_file_data_sampling.attr,
|
||||
&dev_attr_indirect_target_selection.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@@ -508,6 +508,17 @@ static void loop_assign_backing_file(struct loop_device *lo, struct file *file)
|
||||
lo->old_gfp_mask & ~(__GFP_IO | __GFP_FS));
|
||||
}
|
||||
|
||||
static int loop_check_backing_file(struct file *file)
|
||||
{
|
||||
if (!file->f_op->read_iter)
|
||||
return -EINVAL;
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) && !file->f_op->write_iter)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* loop_change_fd switched the backing store of a loopback device to
|
||||
* a new file. This is useful for operating system installers to free up
|
||||
@@ -528,6 +539,10 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
|
||||
if (!file)
|
||||
return -EBADF;
|
||||
|
||||
error = loop_check_backing_file(file);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* suppress uevents while reconfiguring the device */
|
||||
dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1);
|
||||
|
||||
@@ -960,6 +975,14 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
|
||||
|
||||
if (!file)
|
||||
return -EBADF;
|
||||
|
||||
if ((mode & BLK_OPEN_WRITE) && !file->f_op->write_iter)
|
||||
return -EINVAL;
|
||||
|
||||
error = loop_check_backing_file(file);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
is_loop = is_loop_device(file);
|
||||
|
||||
/* This is safe, since we have a reference from open(). */
|
||||
|
||||
@@ -1329,15 +1329,8 @@ int btmtk_usb_setup(struct hci_dev *hdev)
|
||||
fwname = FIRMWARE_MT7668;
|
||||
break;
|
||||
case 0x7922:
|
||||
case 0x7961:
|
||||
case 0x7925:
|
||||
/* Reset the device to ensure it's in the initial state before
|
||||
* downloading the firmware to ensure.
|
||||
*/
|
||||
|
||||
if (!test_bit(BTMTK_FIRMWARE_LOADED, &btmtk_data->flags))
|
||||
btmtk_usb_subsys_reset(hdev, dev_id);
|
||||
|
||||
case 0x7961:
|
||||
btmtk_fw_get_filename(fw_bin_name, sizeof(fw_bin_name), dev_id,
|
||||
fw_version, fw_flavor);
|
||||
|
||||
@@ -1345,12 +1338,9 @@ int btmtk_usb_setup(struct hci_dev *hdev)
|
||||
btmtk_usb_hci_wmt_sync);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
|
||||
clear_bit(BTMTK_FIRMWARE_LOADED, &btmtk_data->flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
set_bit(BTMTK_FIRMWARE_LOADED, &btmtk_data->flags);
|
||||
|
||||
/* It's Device EndPoint Reset Option Register */
|
||||
err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT,
|
||||
MTK_EP_RST_IN_OUT_OPT);
|
||||
|
||||
@@ -1027,27 +1027,28 @@ static const struct panel_desc auo_g070vvn01 = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct drm_display_mode auo_g101evn010_mode = {
|
||||
.clock = 68930,
|
||||
.hdisplay = 1280,
|
||||
.hsync_start = 1280 + 82,
|
||||
.hsync_end = 1280 + 82 + 2,
|
||||
.htotal = 1280 + 82 + 2 + 84,
|
||||
.vdisplay = 800,
|
||||
.vsync_start = 800 + 8,
|
||||
.vsync_end = 800 + 8 + 2,
|
||||
.vtotal = 800 + 8 + 2 + 6,
|
||||
static const struct display_timing auo_g101evn010_timing = {
|
||||
.pixelclock = { 64000000, 68930000, 85000000 },
|
||||
.hactive = { 1280, 1280, 1280 },
|
||||
.hfront_porch = { 8, 64, 256 },
|
||||
.hback_porch = { 8, 64, 256 },
|
||||
.hsync_len = { 40, 168, 767 },
|
||||
.vactive = { 800, 800, 800 },
|
||||
.vfront_porch = { 4, 8, 100 },
|
||||
.vback_porch = { 4, 8, 100 },
|
||||
.vsync_len = { 8, 16, 223 },
|
||||
};
|
||||
|
||||
static const struct panel_desc auo_g101evn010 = {
|
||||
.modes = &auo_g101evn010_mode,
|
||||
.num_modes = 1,
|
||||
.timings = &auo_g101evn010_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 6,
|
||||
.size = {
|
||||
.width = 216,
|
||||
.height = 135,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
|
||||
@@ -43,12 +43,14 @@ static void read_l3cc_table(struct xe_gt *gt,
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
u32 l3cc, l3cc_expected;
|
||||
unsigned int i;
|
||||
unsigned int fw_ref, i;
|
||||
u32 reg_val;
|
||||
u32 ret;
|
||||
|
||||
ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Forcewake Failed.\n");
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
KUNIT_ASSERT_TRUE_MSG(test, true, "Forcewake Failed.\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < info->num_mocs_regs; i++) {
|
||||
if (!(i & 1)) {
|
||||
@@ -72,7 +74,7 @@ static void read_l3cc_table(struct xe_gt *gt,
|
||||
KUNIT_EXPECT_EQ_MSG(test, l3cc_expected, l3cc,
|
||||
"l3cc idx=%u has incorrect val.\n", i);
|
||||
}
|
||||
xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
static void read_mocs_table(struct xe_gt *gt,
|
||||
@@ -80,15 +82,14 @@ static void read_mocs_table(struct xe_gt *gt,
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
u32 mocs, mocs_expected;
|
||||
unsigned int i;
|
||||
unsigned int fw_ref, i;
|
||||
u32 reg_val;
|
||||
u32 ret;
|
||||
|
||||
KUNIT_EXPECT_TRUE_MSG(test, info->unused_entries_index,
|
||||
"Unused entries index should have been defined\n");
|
||||
|
||||
ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Forcewake Failed.\n");
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
KUNIT_ASSERT_NE_MSG(test, fw_ref, 0, "Forcewake Failed.\n");
|
||||
|
||||
for (i = 0; i < info->num_mocs_regs; i++) {
|
||||
if (regs_are_mcr(gt))
|
||||
@@ -106,7 +107,7 @@ static void read_mocs_table(struct xe_gt *gt,
|
||||
"mocs reg 0x%x has incorrect val.\n", i);
|
||||
}
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
static int mocs_kernel_test_run_device(struct xe_device *xe)
|
||||
|
||||
@@ -4474,7 +4474,8 @@ static void nvme_fw_act_work(struct work_struct *work)
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
|
||||
if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_CONNECTING) ||
|
||||
!nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE))
|
||||
return;
|
||||
|
||||
nvme_unquiesce_io_queues(ctrl);
|
||||
|
||||
@@ -748,7 +748,7 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq)
|
||||
return 0;
|
||||
mnt = real_mount(bastard);
|
||||
mnt_add_count(mnt, 1);
|
||||
smp_mb(); // see mntput_no_expire()
|
||||
smp_mb(); // see mntput_no_expire() and do_umount()
|
||||
if (likely(!read_seqretry(&mount_lock, seq)))
|
||||
return 0;
|
||||
if (bastard->mnt_flags & MNT_SYNC_UMOUNT) {
|
||||
@@ -1917,6 +1917,7 @@ static int do_umount(struct mount *mnt, int flags)
|
||||
umount_tree(mnt, UMOUNT_PROPAGATE);
|
||||
retval = 0;
|
||||
} else {
|
||||
smp_mb(); // paired with __legitimize_mnt()
|
||||
shrink_submounts(mnt);
|
||||
retval = -EBUSY;
|
||||
if (!propagate_mount_busy(mnt, 2)) {
|
||||
|
||||
@@ -77,6 +77,8 @@ extern ssize_t cpu_show_gds(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
extern ssize_t cpu_show_reg_file_data_sampling(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
extern ssize_t cpu_show_indirect_target_selection(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
extern __printf(4, 5)
|
||||
struct device *cpu_device_create(struct device *parent, void *drvdata,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/cleanup.h>
|
||||
|
||||
#if (defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)) && \
|
||||
!defined(CONFIG_KASAN_VMALLOC)
|
||||
@@ -123,6 +124,8 @@ void *execmem_alloc(enum execmem_type type, size_t size);
|
||||
*/
|
||||
void execmem_free(void *ptr);
|
||||
|
||||
DEFINE_FREE(execmem, void *, if (_T) execmem_free(_T));
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
/**
|
||||
* execmem_vmap - create virtual mapping for EXECMEM_MODULE_DATA memory
|
||||
|
||||
@@ -586,6 +586,11 @@ struct module {
|
||||
atomic_t refcnt;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MITIGATION_ITS
|
||||
int its_num_pages;
|
||||
void **its_page_array;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CONSTRUCTORS
|
||||
/* Constructor functions. */
|
||||
ctor_fn_t *ctors;
|
||||
|
||||
@@ -441,24 +441,6 @@ static struct io_kiocb *__io_prep_linked_timeout(struct io_kiocb *req)
|
||||
return req->link;
|
||||
}
|
||||
|
||||
static inline struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req)
|
||||
{
|
||||
if (likely(!(req->flags & REQ_F_ARM_LTIMEOUT)))
|
||||
return NULL;
|
||||
return __io_prep_linked_timeout(req);
|
||||
}
|
||||
|
||||
static noinline void __io_arm_ltimeout(struct io_kiocb *req)
|
||||
{
|
||||
io_queue_linked_timeout(__io_prep_linked_timeout(req));
|
||||
}
|
||||
|
||||
static inline void io_arm_ltimeout(struct io_kiocb *req)
|
||||
{
|
||||
if (unlikely(req->flags & REQ_F_ARM_LTIMEOUT))
|
||||
__io_arm_ltimeout(req);
|
||||
}
|
||||
|
||||
static void io_prep_async_work(struct io_kiocb *req)
|
||||
{
|
||||
const struct io_issue_def *def = &io_issue_defs[req->opcode];
|
||||
@@ -511,7 +493,6 @@ static void io_prep_async_link(struct io_kiocb *req)
|
||||
|
||||
static void io_queue_iowq(struct io_kiocb *req)
|
||||
{
|
||||
struct io_kiocb *link = io_prep_linked_timeout(req);
|
||||
struct io_uring_task *tctx = req->task->io_uring;
|
||||
|
||||
BUG_ON(!tctx);
|
||||
@@ -536,8 +517,6 @@ static void io_queue_iowq(struct io_kiocb *req)
|
||||
|
||||
trace_io_uring_queue_async_work(req, io_wq_is_hashed(&req->work));
|
||||
io_wq_enqueue(tctx->io_wq, &req->work);
|
||||
if (link)
|
||||
io_queue_linked_timeout(link);
|
||||
}
|
||||
|
||||
static void io_req_queue_iowq_tw(struct io_kiocb *req, struct io_tw_state *ts)
|
||||
@@ -1731,17 +1710,24 @@ static bool io_assign_file(struct io_kiocb *req, const struct io_issue_def *def,
|
||||
return !!req->file;
|
||||
}
|
||||
|
||||
#define REQ_ISSUE_SLOW_FLAGS (REQ_F_CREDS | REQ_F_ARM_LTIMEOUT)
|
||||
|
||||
static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
const struct io_issue_def *def = &io_issue_defs[req->opcode];
|
||||
const struct cred *creds = NULL;
|
||||
struct io_kiocb *link = NULL;
|
||||
int ret;
|
||||
|
||||
if (unlikely(!io_assign_file(req, def, issue_flags)))
|
||||
return -EBADF;
|
||||
|
||||
if (unlikely((req->flags & REQ_F_CREDS) && req->creds != current_cred()))
|
||||
creds = override_creds(req->creds);
|
||||
if (unlikely(req->flags & REQ_ISSUE_SLOW_FLAGS)) {
|
||||
if ((req->flags & REQ_F_CREDS) && req->creds != current_cred())
|
||||
creds = override_creds(req->creds);
|
||||
if (req->flags & REQ_F_ARM_LTIMEOUT)
|
||||
link = __io_prep_linked_timeout(req);
|
||||
}
|
||||
|
||||
if (!def->audit_skip)
|
||||
audit_uring_entry(req->opcode);
|
||||
@@ -1751,8 +1737,12 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
|
||||
if (!def->audit_skip)
|
||||
audit_uring_exit(!ret, ret);
|
||||
|
||||
if (creds)
|
||||
revert_creds(creds);
|
||||
if (unlikely(creds || link)) {
|
||||
if (creds)
|
||||
revert_creds(creds);
|
||||
if (link)
|
||||
io_queue_linked_timeout(link);
|
||||
}
|
||||
|
||||
if (ret == IOU_OK) {
|
||||
if (issue_flags & IO_URING_F_COMPLETE_DEFER)
|
||||
@@ -1765,7 +1755,6 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
||||
if (ret == IOU_ISSUE_SKIP_COMPLETE) {
|
||||
ret = 0;
|
||||
io_arm_ltimeout(req);
|
||||
|
||||
/* If the op doesn't have a file, we're not polling for it */
|
||||
if ((req->ctx->flags & IORING_SETUP_IOPOLL) && def->iopoll_queue)
|
||||
@@ -1808,8 +1797,6 @@ void io_wq_submit_work(struct io_wq_work *work)
|
||||
else
|
||||
req_ref_get(req);
|
||||
|
||||
io_arm_ltimeout(req);
|
||||
|
||||
/* either cancelled or io-wq is dying, so don't touch tctx->iowq */
|
||||
if (atomic_read(&work->flags) & IO_WQ_WORK_CANCEL) {
|
||||
fail:
|
||||
@@ -1929,15 +1916,11 @@ struct file *io_file_get_normal(struct io_kiocb *req, int fd)
|
||||
static void io_queue_async(struct io_kiocb *req, int ret)
|
||||
__must_hold(&req->ctx->uring_lock)
|
||||
{
|
||||
struct io_kiocb *linked_timeout;
|
||||
|
||||
if (ret != -EAGAIN || (req->flags & REQ_F_NOWAIT)) {
|
||||
io_req_defer_failed(req, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
linked_timeout = io_prep_linked_timeout(req);
|
||||
|
||||
switch (io_arm_poll_handler(req, 0)) {
|
||||
case IO_APOLL_READY:
|
||||
io_kbuf_recycle(req, 0);
|
||||
@@ -1950,9 +1933,6 @@ static void io_queue_async(struct io_kiocb *req, int ret)
|
||||
case IO_APOLL_OK:
|
||||
break;
|
||||
}
|
||||
|
||||
if (linked_timeout)
|
||||
io_queue_linked_timeout(linked_timeout);
|
||||
}
|
||||
|
||||
static inline void io_queue_sqe(struct io_kiocb *req)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "sqpoll.h"
|
||||
|
||||
#define IORING_SQPOLL_CAP_ENTRIES_VALUE 8
|
||||
#define IORING_TW_CAP_ENTRIES_VALUE 8
|
||||
#define IORING_TW_CAP_ENTRIES_VALUE 32
|
||||
|
||||
enum {
|
||||
IO_SQ_THREAD_SHOULD_STOP = 0,
|
||||
|
||||
@@ -7283,9 +7283,6 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags)
|
||||
idle_h_nr_running = task_has_idle_policy(p);
|
||||
if (!task_sleep && !task_delayed)
|
||||
h_nr_delayed = !!se->sched_delayed;
|
||||
} else {
|
||||
cfs_rq = group_cfs_rq(se);
|
||||
slice = cfs_rq_min_slice(cfs_rq);
|
||||
}
|
||||
|
||||
for_each_sched_entity(se) {
|
||||
@@ -7295,6 +7292,7 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags)
|
||||
if (p && &p->se == se)
|
||||
return -1;
|
||||
|
||||
slice = cfs_rq_min_slice(cfs_rq);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
159
mm/page_alloc.c
159
mm/page_alloc.c
@@ -2151,13 +2151,12 @@ static inline bool boost_watermark(struct zone *zone)
|
||||
* can claim the whole pageblock for the requested migratetype. If not, we check
|
||||
* the pageblock for constituent pages; if at least half of the pages are free
|
||||
* or compatible, we can still claim the whole block, so pages freed in the
|
||||
* future will be put on the correct free list. Otherwise, we isolate exactly
|
||||
* the order we need from the fallback block and leave its migratetype alone.
|
||||
* future will be put on the correct free list.
|
||||
*/
|
||||
static struct page *
|
||||
steal_suitable_fallback(struct zone *zone, struct page *page,
|
||||
int current_order, int order, int start_type,
|
||||
unsigned int alloc_flags, bool whole_block)
|
||||
try_to_steal_block(struct zone *zone, struct page *page,
|
||||
int current_order, int order, int start_type,
|
||||
unsigned int alloc_flags)
|
||||
{
|
||||
int free_pages, movable_pages, alike_pages;
|
||||
unsigned long start_pfn;
|
||||
@@ -2170,7 +2169,7 @@ steal_suitable_fallback(struct zone *zone, struct page *page,
|
||||
* highatomic accounting.
|
||||
*/
|
||||
if (is_migrate_highatomic(block_type))
|
||||
goto single_page;
|
||||
return NULL;
|
||||
|
||||
/* Take ownership for orders >= pageblock_order */
|
||||
if (current_order >= pageblock_order) {
|
||||
@@ -2191,14 +2190,10 @@ steal_suitable_fallback(struct zone *zone, struct page *page,
|
||||
if (boost_watermark(zone) && (alloc_flags & ALLOC_KSWAPD))
|
||||
set_bit(ZONE_BOOSTED_WATERMARK, &zone->flags);
|
||||
|
||||
/* We are not allowed to try stealing from the whole block */
|
||||
if (!whole_block)
|
||||
goto single_page;
|
||||
|
||||
/* moving whole block can fail due to zone boundary conditions */
|
||||
if (!prep_move_freepages_block(zone, page, &start_pfn, &free_pages,
|
||||
&movable_pages))
|
||||
goto single_page;
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Determine how many pages are compatible with our allocation.
|
||||
@@ -2231,9 +2226,7 @@ steal_suitable_fallback(struct zone *zone, struct page *page,
|
||||
return __rmqueue_smallest(zone, order, start_type);
|
||||
}
|
||||
|
||||
single_page:
|
||||
page_del_and_expand(zone, page, order, current_order, block_type);
|
||||
return page;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2425,17 +2418,15 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
|
||||
}
|
||||
|
||||
/*
|
||||
* Try finding a free buddy page on the fallback list and put it on the free
|
||||
* list of requested migratetype, possibly along with other pages from the same
|
||||
* block, depending on fragmentation avoidance heuristics. Returns true if
|
||||
* fallback was found so that __rmqueue_smallest() can grab it.
|
||||
* Try to allocate from some fallback migratetype by claiming the entire block,
|
||||
* i.e. converting it to the allocation's start migratetype.
|
||||
*
|
||||
* The use of signed ints for order and current_order is a deliberate
|
||||
* deviation from the rest of this file, to make the for loop
|
||||
* condition simpler.
|
||||
*/
|
||||
static __always_inline struct page *
|
||||
__rmqueue_fallback(struct zone *zone, int order, int start_migratetype,
|
||||
__rmqueue_claim(struct zone *zone, int order, int start_migratetype,
|
||||
unsigned int alloc_flags)
|
||||
{
|
||||
struct free_area *area;
|
||||
@@ -2466,58 +2457,66 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype,
|
||||
if (fallback_mt == -1)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We cannot steal all free pages from the pageblock and the
|
||||
* requested migratetype is movable. In that case it's better to
|
||||
* steal and split the smallest available page instead of the
|
||||
* largest available page, because even if the next movable
|
||||
* allocation falls back into a different pageblock than this
|
||||
* one, it won't cause permanent fragmentation.
|
||||
*/
|
||||
if (!can_steal && start_migratetype == MIGRATE_MOVABLE
|
||||
&& current_order > order)
|
||||
goto find_smallest;
|
||||
if (!can_steal)
|
||||
break;
|
||||
|
||||
goto do_steal;
|
||||
page = get_page_from_free_area(area, fallback_mt);
|
||||
page = try_to_steal_block(zone, page, current_order, order,
|
||||
start_migratetype, alloc_flags);
|
||||
if (page) {
|
||||
trace_mm_page_alloc_extfrag(page, order, current_order,
|
||||
start_migratetype, fallback_mt);
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to steal a single page from some fallback migratetype. Leave the rest of
|
||||
* the block as its current migratetype, potentially causing fragmentation.
|
||||
*/
|
||||
static __always_inline struct page *
|
||||
__rmqueue_steal(struct zone *zone, int order, int start_migratetype)
|
||||
{
|
||||
struct free_area *area;
|
||||
int current_order;
|
||||
struct page *page;
|
||||
int fallback_mt;
|
||||
bool can_steal;
|
||||
|
||||
find_smallest:
|
||||
for (current_order = order; current_order < NR_PAGE_ORDERS; current_order++) {
|
||||
area = &(zone->free_area[current_order]);
|
||||
fallback_mt = find_suitable_fallback(area, current_order,
|
||||
start_migratetype, false, &can_steal);
|
||||
if (fallback_mt != -1)
|
||||
break;
|
||||
if (fallback_mt == -1)
|
||||
continue;
|
||||
|
||||
page = get_page_from_free_area(area, fallback_mt);
|
||||
page_del_and_expand(zone, page, order, current_order, fallback_mt);
|
||||
trace_mm_page_alloc_extfrag(page, order, current_order,
|
||||
start_migratetype, fallback_mt);
|
||||
return page;
|
||||
}
|
||||
|
||||
/*
|
||||
* This should not happen - we already found a suitable fallback
|
||||
* when looking for the largest page.
|
||||
*/
|
||||
VM_BUG_ON(current_order > MAX_PAGE_ORDER);
|
||||
|
||||
do_steal:
|
||||
page = get_page_from_free_area(area, fallback_mt);
|
||||
|
||||
/* take off list, maybe claim block, expand remainder */
|
||||
page = steal_suitable_fallback(zone, page, current_order, order,
|
||||
start_migratetype, alloc_flags, can_steal);
|
||||
|
||||
trace_mm_page_alloc_extfrag(page, order, current_order,
|
||||
start_migratetype, fallback_mt);
|
||||
|
||||
return page;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum rmqueue_mode {
|
||||
RMQUEUE_NORMAL,
|
||||
RMQUEUE_CMA,
|
||||
RMQUEUE_CLAIM,
|
||||
RMQUEUE_STEAL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Do the hard work of removing an element from the buddy allocator.
|
||||
* Call me with the zone->lock already held.
|
||||
*/
|
||||
static __always_inline struct page *
|
||||
__rmqueue(struct zone *zone, unsigned int order, int migratetype,
|
||||
unsigned int alloc_flags)
|
||||
unsigned int alloc_flags, enum rmqueue_mode *mode)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
|
||||
@@ -2540,16 +2539,49 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype,
|
||||
}
|
||||
}
|
||||
|
||||
page = __rmqueue_smallest(zone, order, migratetype);
|
||||
if (unlikely(!page)) {
|
||||
if (!cma_redirect_restricted() && alloc_flags & ALLOC_CMA)
|
||||
/*
|
||||
* First try the freelists of the requested migratetype, then try
|
||||
* fallbacks modes with increasing levels of fragmentation risk.
|
||||
*
|
||||
* The fallback logic is expensive and rmqueue_bulk() calls in
|
||||
* a loop with the zone->lock held, meaning the freelists are
|
||||
* not subject to any outside changes. Remember in *mode where
|
||||
* we found pay dirt, to save us the search on the next call.
|
||||
*/
|
||||
switch (*mode) {
|
||||
case RMQUEUE_NORMAL:
|
||||
page = __rmqueue_smallest(zone, order, migratetype);
|
||||
if (page)
|
||||
return page;
|
||||
fallthrough;
|
||||
case RMQUEUE_CMA:
|
||||
if (!cma_redirect_restricted() && alloc_flags & ALLOC_CMA) {
|
||||
page = __rmqueue_cma_fallback(zone, order);
|
||||
|
||||
if (!page)
|
||||
page = __rmqueue_fallback(zone, order, migratetype,
|
||||
alloc_flags);
|
||||
if (page) {
|
||||
*mode = RMQUEUE_CMA;
|
||||
return page;
|
||||
}
|
||||
}
|
||||
fallthrough;
|
||||
case RMQUEUE_CLAIM:
|
||||
page = __rmqueue_claim(zone, order, migratetype, alloc_flags);
|
||||
if (page) {
|
||||
/* Replenished preferred freelist, back to normal mode. */
|
||||
*mode = RMQUEUE_NORMAL;
|
||||
return page;
|
||||
}
|
||||
fallthrough;
|
||||
case RMQUEUE_STEAL:
|
||||
if (!(alloc_flags & ALLOC_NOFRAGMENT)) {
|
||||
page = __rmqueue_steal(zone, order, migratetype);
|
||||
if (page) {
|
||||
*mode = RMQUEUE_STEAL;
|
||||
return page;
|
||||
}
|
||||
}
|
||||
}
|
||||
return page;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2561,6 +2593,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
|
||||
unsigned long count, struct list_head *list,
|
||||
int migratetype, unsigned int alloc_flags)
|
||||
{
|
||||
enum rmqueue_mode rmqm = RMQUEUE_NORMAL;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
@@ -2576,7 +2609,8 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
|
||||
if (cma_redirect_restricted() && is_migrate_cma(migratetype))
|
||||
page = __rmqueue_cma_fallback(zone, order);
|
||||
else
|
||||
page = __rmqueue(zone, order, migratetype, alloc_flags);
|
||||
page = __rmqueue(zone, order, migratetype, alloc_flags,
|
||||
&rmqm);
|
||||
|
||||
if (unlikely(page == NULL))
|
||||
break;
|
||||
@@ -3196,13 +3230,14 @@ struct page *rmqueue_buddy(struct zone *preferred_zone, struct zone *zone,
|
||||
if (alloc_flags & ALLOC_HIGHATOMIC)
|
||||
page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
|
||||
if (!page) {
|
||||
enum rmqueue_mode rmqm = RMQUEUE_NORMAL;
|
||||
if (cma_redirect_restricted() &&
|
||||
alloc_flags & ALLOC_CMA)
|
||||
page = __rmqueue_cma_fallback(zone, order);
|
||||
|
||||
if (!page)
|
||||
page = __rmqueue(zone, order, migratetype,
|
||||
alloc_flags);
|
||||
alloc_flags, &rmqm);
|
||||
/*
|
||||
* If the allocation fails, allow OOM handling and
|
||||
* order-0 (atomic) allocs access to HIGHATOMIC
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
//! Implementation of [`Vec`].
|
||||
|
||||
// May not be needed in Rust 1.87.0 (pending beta backport).
|
||||
#![allow(clippy::ptr_eq)]
|
||||
|
||||
use super::{
|
||||
allocator::{KVmalloc, Kmalloc, Vmalloc},
|
||||
layout::ArrayLayout,
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
//! A linked list implementation.
|
||||
|
||||
// May not be needed in Rust 1.87.0 (pending beta backport).
|
||||
#![allow(clippy::ptr_eq)]
|
||||
|
||||
use crate::init::PinInit;
|
||||
use crate::sync::ArcBorrow;
|
||||
use crate::types::Opaque;
|
||||
|
||||
@@ -55,7 +55,7 @@ impl fmt::Display for BStr {
|
||||
b'\r' => f.write_str("\\r")?,
|
||||
// Printable characters.
|
||||
0x20..=0x7e => f.write_char(b as char)?,
|
||||
_ => write!(f, "\\x{:02x}", b)?,
|
||||
_ => write!(f, "\\x{b:02x}")?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -90,7 +90,7 @@ impl fmt::Debug for BStr {
|
||||
b'\\' => f.write_str("\\\\")?,
|
||||
// Printable characters.
|
||||
0x20..=0x7e => f.write_char(b as char)?,
|
||||
_ => write!(f, "\\x{:02x}", b)?,
|
||||
_ => write!(f, "\\x{b:02x}")?,
|
||||
}
|
||||
}
|
||||
f.write_char('"')
|
||||
@@ -397,7 +397,7 @@ impl fmt::Display for CStr {
|
||||
// Printable character.
|
||||
f.write_char(c as char)?;
|
||||
} else {
|
||||
write!(f, "\\x{:02x}", c)?;
|
||||
write!(f, "\\x{c:02x}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -428,7 +428,7 @@ impl fmt::Debug for CStr {
|
||||
// Printable characters.
|
||||
b'\"' => f.write_str("\\\"")?,
|
||||
0x20..=0x7e => f.write_char(c as char)?,
|
||||
_ => write!(f, "\\x{:02x}", c)?,
|
||||
_ => write!(f, "\\x{c:02x}")?,
|
||||
}
|
||||
}
|
||||
f.write_str("\"")
|
||||
@@ -588,13 +588,13 @@ mod tests {
|
||||
#[test]
|
||||
fn test_cstr_display() {
|
||||
let hello_world = CStr::from_bytes_with_nul(b"hello, world!\0").unwrap();
|
||||
assert_eq!(format!("{}", hello_world), "hello, world!");
|
||||
assert_eq!(format!("{hello_world}"), "hello, world!");
|
||||
let non_printables = CStr::from_bytes_with_nul(b"\x01\x09\x0a\0").unwrap();
|
||||
assert_eq!(format!("{}", non_printables), "\\x01\\x09\\x0a");
|
||||
assert_eq!(format!("{non_printables}"), "\\x01\\x09\\x0a");
|
||||
let non_ascii = CStr::from_bytes_with_nul(b"d\xe9j\xe0 vu\0").unwrap();
|
||||
assert_eq!(format!("{}", non_ascii), "d\\xe9j\\xe0 vu");
|
||||
assert_eq!(format!("{non_ascii}"), "d\\xe9j\\xe0 vu");
|
||||
let good_bytes = CStr::from_bytes_with_nul(b"\xf0\x9f\xa6\x80\0").unwrap();
|
||||
assert_eq!(format!("{}", good_bytes), "\\xf0\\x9f\\xa6\\x80");
|
||||
assert_eq!(format!("{good_bytes}"), "\\xf0\\x9f\\xa6\\x80");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -605,47 +605,47 @@ mod tests {
|
||||
bytes[i as usize] = i.wrapping_add(1);
|
||||
}
|
||||
let cstr = CStr::from_bytes_with_nul(&bytes).unwrap();
|
||||
assert_eq!(format!("{}", cstr), ALL_ASCII_CHARS);
|
||||
assert_eq!(format!("{cstr}"), ALL_ASCII_CHARS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cstr_debug() {
|
||||
let hello_world = CStr::from_bytes_with_nul(b"hello, world!\0").unwrap();
|
||||
assert_eq!(format!("{:?}", hello_world), "\"hello, world!\"");
|
||||
assert_eq!(format!("{hello_world:?}"), "\"hello, world!\"");
|
||||
let non_printables = CStr::from_bytes_with_nul(b"\x01\x09\x0a\0").unwrap();
|
||||
assert_eq!(format!("{:?}", non_printables), "\"\\x01\\x09\\x0a\"");
|
||||
assert_eq!(format!("{non_printables:?}"), "\"\\x01\\x09\\x0a\"");
|
||||
let non_ascii = CStr::from_bytes_with_nul(b"d\xe9j\xe0 vu\0").unwrap();
|
||||
assert_eq!(format!("{:?}", non_ascii), "\"d\\xe9j\\xe0 vu\"");
|
||||
assert_eq!(format!("{non_ascii:?}"), "\"d\\xe9j\\xe0 vu\"");
|
||||
let good_bytes = CStr::from_bytes_with_nul(b"\xf0\x9f\xa6\x80\0").unwrap();
|
||||
assert_eq!(format!("{:?}", good_bytes), "\"\\xf0\\x9f\\xa6\\x80\"");
|
||||
assert_eq!(format!("{good_bytes:?}"), "\"\\xf0\\x9f\\xa6\\x80\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bstr_display() {
|
||||
let hello_world = BStr::from_bytes(b"hello, world!");
|
||||
assert_eq!(format!("{}", hello_world), "hello, world!");
|
||||
assert_eq!(format!("{hello_world}"), "hello, world!");
|
||||
let escapes = BStr::from_bytes(b"_\t_\n_\r_\\_\'_\"_");
|
||||
assert_eq!(format!("{}", escapes), "_\\t_\\n_\\r_\\_'_\"_");
|
||||
assert_eq!(format!("{escapes}"), "_\\t_\\n_\\r_\\_'_\"_");
|
||||
let others = BStr::from_bytes(b"\x01");
|
||||
assert_eq!(format!("{}", others), "\\x01");
|
||||
assert_eq!(format!("{others}"), "\\x01");
|
||||
let non_ascii = BStr::from_bytes(b"d\xe9j\xe0 vu");
|
||||
assert_eq!(format!("{}", non_ascii), "d\\xe9j\\xe0 vu");
|
||||
assert_eq!(format!("{non_ascii}"), "d\\xe9j\\xe0 vu");
|
||||
let good_bytes = BStr::from_bytes(b"\xf0\x9f\xa6\x80");
|
||||
assert_eq!(format!("{}", good_bytes), "\\xf0\\x9f\\xa6\\x80");
|
||||
assert_eq!(format!("{good_bytes}"), "\\xf0\\x9f\\xa6\\x80");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bstr_debug() {
|
||||
let hello_world = BStr::from_bytes(b"hello, world!");
|
||||
assert_eq!(format!("{:?}", hello_world), "\"hello, world!\"");
|
||||
assert_eq!(format!("{hello_world:?}"), "\"hello, world!\"");
|
||||
let escapes = BStr::from_bytes(b"_\t_\n_\r_\\_\'_\"_");
|
||||
assert_eq!(format!("{:?}", escapes), "\"_\\t_\\n_\\r_\\\\_'_\\\"_\"");
|
||||
assert_eq!(format!("{escapes:?}"), "\"_\\t_\\n_\\r_\\\\_'_\\\"_\"");
|
||||
let others = BStr::from_bytes(b"\x01");
|
||||
assert_eq!(format!("{:?}", others), "\"\\x01\"");
|
||||
assert_eq!(format!("{others:?}"), "\"\\x01\"");
|
||||
let non_ascii = BStr::from_bytes(b"d\xe9j\xe0 vu");
|
||||
assert_eq!(format!("{:?}", non_ascii), "\"d\\xe9j\\xe0 vu\"");
|
||||
assert_eq!(format!("{non_ascii:?}"), "\"d\\xe9j\\xe0 vu\"");
|
||||
let good_bytes = BStr::from_bytes(b"\xf0\x9f\xa6\x80");
|
||||
assert_eq!(format!("{:?}", good_bytes), "\"\\xf0\\x9f\\xa6\\x80\"");
|
||||
assert_eq!(format!("{good_bytes:?}"), "\"\\xf0\\x9f\\xa6\\x80\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ impl<'a> ModInfoBuilder<'a> {
|
||||
)
|
||||
} else {
|
||||
// Loadable modules' modinfo strings go as-is.
|
||||
format!("{field}={content}\0", field = field, content = content)
|
||||
format!("{field}={content}\0")
|
||||
};
|
||||
|
||||
write!(
|
||||
@@ -124,10 +124,7 @@ impl ModuleInfo {
|
||||
};
|
||||
|
||||
if seen_keys.contains(&key) {
|
||||
panic!(
|
||||
"Duplicated key \"{}\". Keys can only be specified once.",
|
||||
key
|
||||
);
|
||||
panic!("Duplicated key \"{key}\". Keys can only be specified once.");
|
||||
}
|
||||
|
||||
assert_eq!(expect_punct(it), ':');
|
||||
@@ -140,10 +137,7 @@ impl ModuleInfo {
|
||||
"license" => info.license = expect_string_ascii(it),
|
||||
"alias" => info.alias = Some(expect_string_array(it)),
|
||||
"firmware" => info.firmware = Some(expect_string_array(it)),
|
||||
_ => panic!(
|
||||
"Unknown key \"{}\". Valid keys are: {:?}.",
|
||||
key, EXPECTED_KEYS
|
||||
),
|
||||
_ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
|
||||
}
|
||||
|
||||
assert_eq!(expect_punct(it), ',');
|
||||
@@ -155,7 +149,7 @@ impl ModuleInfo {
|
||||
|
||||
for key in REQUIRED_KEYS {
|
||||
if !seen_keys.iter().any(|e| e == key) {
|
||||
panic!("Missing required key \"{}\".", key);
|
||||
panic!("Missing required key \"{key}\".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,10 +161,7 @@ impl ModuleInfo {
|
||||
}
|
||||
|
||||
if seen_keys != ordered_keys {
|
||||
panic!(
|
||||
"Keys are not ordered as expected. Order them like: {:?}.",
|
||||
ordered_keys
|
||||
);
|
||||
panic!("Keys are not ordered as expected. Order them like: {ordered_keys:?}.");
|
||||
}
|
||||
|
||||
info
|
||||
|
||||
@@ -25,8 +25,7 @@ pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream
|
||||
// Found the end of the generics, this should be `PinnedDrop`.
|
||||
assert!(
|
||||
matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"),
|
||||
"expected 'PinnedDrop', found: '{:?}'",
|
||||
tt
|
||||
"expected 'PinnedDrop', found: '{tt:?}'"
|
||||
);
|
||||
pinned_drop_idx = Some(i);
|
||||
break;
|
||||
|
||||
@@ -115,6 +115,7 @@ TARGETS += user_events
|
||||
TARGETS += vDSO
|
||||
TARGETS += mm
|
||||
TARGETS += x86
|
||||
TARGETS += x86/bugs
|
||||
TARGETS += zram
|
||||
#Please keep the TARGETS list alphabetically sorted
|
||||
# Run "make quicktest=1 run_tests" or
|
||||
|
||||
3
tools/testing/selftests/x86/bugs/Makefile
Normal file
3
tools/testing/selftests/x86/bugs/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
TEST_PROGS := its_sysfs.py its_permutations.py its_indirect_alignment.py its_ret_alignment.py
|
||||
TEST_FILES := common.py
|
||||
include ../../lib.mk
|
||||
164
tools/testing/selftests/x86/bugs/common.py
Normal file
164
tools/testing/selftests/x86/bugs/common.py
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Intel Corporation
|
||||
#
|
||||
# This contains kselftest framework adapted common functions for testing
|
||||
# mitigation for x86 bugs.
|
||||
|
||||
import os, sys, re, shutil
|
||||
|
||||
sys.path.insert(0, '../../kselftest')
|
||||
import ksft
|
||||
|
||||
def read_file(path):
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
with open(path, 'r') as file:
|
||||
return file.read().strip()
|
||||
|
||||
def cpuinfo_has(arg):
|
||||
cpuinfo = read_file('/proc/cpuinfo')
|
||||
if arg in cpuinfo:
|
||||
return True
|
||||
return False
|
||||
|
||||
def cmdline_has(arg):
|
||||
cmdline = read_file('/proc/cmdline')
|
||||
if arg in cmdline:
|
||||
return True
|
||||
return False
|
||||
|
||||
def cmdline_has_either(args):
|
||||
cmdline = read_file('/proc/cmdline')
|
||||
for arg in args:
|
||||
if arg in cmdline:
|
||||
return True
|
||||
return False
|
||||
|
||||
def cmdline_has_none(args):
|
||||
return not cmdline_has_either(args)
|
||||
|
||||
def cmdline_has_all(args):
|
||||
cmdline = read_file('/proc/cmdline')
|
||||
for arg in args:
|
||||
if arg not in cmdline:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_sysfs(bug):
|
||||
return read_file("/sys/devices/system/cpu/vulnerabilities/" + bug)
|
||||
|
||||
def sysfs_has(bug, mitigation):
|
||||
status = get_sysfs(bug)
|
||||
if mitigation in status:
|
||||
return True
|
||||
return False
|
||||
|
||||
def sysfs_has_either(bugs, mitigations):
|
||||
for bug in bugs:
|
||||
for mitigation in mitigations:
|
||||
if sysfs_has(bug, mitigation):
|
||||
return True
|
||||
return False
|
||||
|
||||
def sysfs_has_none(bugs, mitigations):
|
||||
return not sysfs_has_either(bugs, mitigations)
|
||||
|
||||
def sysfs_has_all(bugs, mitigations):
|
||||
for bug in bugs:
|
||||
for mitigation in mitigations:
|
||||
if not sysfs_has(bug, mitigation):
|
||||
return False
|
||||
return True
|
||||
|
||||
def bug_check_pass(bug, found):
|
||||
ksft.print_msg(f"\nFound: {found}")
|
||||
# ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
|
||||
ksft.test_result_pass(f'{bug}: {found}')
|
||||
|
||||
def bug_check_fail(bug, found, expected):
|
||||
ksft.print_msg(f'\nFound:\t {found}')
|
||||
ksft.print_msg(f'Expected:\t {expected}')
|
||||
ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
|
||||
ksft.test_result_fail(f'{bug}: {found}')
|
||||
|
||||
def bug_status_unknown(bug, found):
|
||||
ksft.print_msg(f'\nUnknown status: {found}')
|
||||
ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
|
||||
ksft.test_result_fail(f'{bug}: {found}')
|
||||
|
||||
def basic_checks_sufficient(bug, mitigation):
|
||||
if not mitigation:
|
||||
bug_status_unknown(bug, "None")
|
||||
return True
|
||||
elif mitigation == "Not affected":
|
||||
ksft.test_result_pass(bug)
|
||||
return True
|
||||
elif mitigation == "Vulnerable":
|
||||
if cmdline_has_either([f'{bug}=off', 'mitigations=off']):
|
||||
bug_check_pass(bug, mitigation)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_section_info(vmlinux, section_name):
|
||||
from elftools.elf.elffile import ELFFile
|
||||
with open(vmlinux, 'rb') as f:
|
||||
elffile = ELFFile(f)
|
||||
section = elffile.get_section_by_name(section_name)
|
||||
if section is None:
|
||||
ksft.print_msg("Available sections in vmlinux:")
|
||||
for sec in elffile.iter_sections():
|
||||
ksft.print_msg(sec.name)
|
||||
raise ValueError(f"Section {section_name} not found in {vmlinux}")
|
||||
return section['sh_addr'], section['sh_offset'], section['sh_size']
|
||||
|
||||
def get_patch_sites(vmlinux, offset, size):
|
||||
import struct
|
||||
output = []
|
||||
with open(vmlinux, 'rb') as f:
|
||||
f.seek(offset)
|
||||
i = 0
|
||||
while i < size:
|
||||
data = f.read(4) # s32
|
||||
if not data:
|
||||
break
|
||||
sym_offset = struct.unpack('<i', data)[0] + i
|
||||
i += 4
|
||||
output.append(sym_offset)
|
||||
return output
|
||||
|
||||
def get_instruction_from_vmlinux(elffile, section, virtual_address, target_address):
|
||||
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
|
||||
section_start = section['sh_addr']
|
||||
section_end = section_start + section['sh_size']
|
||||
|
||||
if not (section_start <= target_address < section_end):
|
||||
return None
|
||||
|
||||
offset = target_address - section_start
|
||||
code = section.data()[offset:offset + 16]
|
||||
|
||||
cap = init_capstone()
|
||||
for instruction in cap.disasm(code, target_address):
|
||||
if instruction.address == target_address:
|
||||
return instruction
|
||||
return None
|
||||
|
||||
def init_capstone():
|
||||
from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_OPT_SYNTAX_ATT
|
||||
cap = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||
cap.syntax = CS_OPT_SYNTAX_ATT
|
||||
return cap
|
||||
|
||||
def get_runtime_kernel():
|
||||
import drgn
|
||||
return drgn.program_from_kernel()
|
||||
|
||||
def check_dependencies_or_skip(modules, script_name="unknown test"):
|
||||
for mod in modules:
|
||||
try:
|
||||
__import__(mod)
|
||||
except ImportError:
|
||||
ksft.test_result_skip(f"Skipping {script_name}: missing module '{mod}'")
|
||||
ksft.finished()
|
||||
150
tools/testing/selftests/x86/bugs/its_indirect_alignment.py
Normal file
150
tools/testing/selftests/x86/bugs/its_indirect_alignment.py
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Intel Corporation
|
||||
#
|
||||
# Test for indirect target selection (ITS) mitigation.
|
||||
#
|
||||
# Test if indirect CALL/JMP are correctly patched by evaluating
|
||||
# the vmlinux .retpoline_sites in /proc/kcore.
|
||||
|
||||
# Install dependencies
|
||||
# add-apt-repository ppa:michel-slm/kernel-utils
|
||||
# apt update
|
||||
# apt install -y python3-drgn python3-pyelftools python3-capstone
|
||||
#
|
||||
# Best to copy the vmlinux at a standard location:
|
||||
# mkdir -p /usr/lib/debug/lib/modules/$(uname -r)
|
||||
# cp $VMLINUX /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
|
||||
#
|
||||
# Usage: ./its_indirect_alignment.py [vmlinux]
|
||||
|
||||
import os, sys, argparse
|
||||
from pathlib import Path
|
||||
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, this_dir + '/../../kselftest')
|
||||
import ksft
|
||||
import common as c
|
||||
|
||||
bug = "indirect_target_selection"
|
||||
|
||||
mitigation = c.get_sysfs(bug)
|
||||
if not mitigation or "Aligned branch/return thunks" not in mitigation:
|
||||
ksft.test_result_skip("Skipping its_indirect_alignment.py: Aligned branch/return thunks not enabled")
|
||||
ksft.finished()
|
||||
|
||||
if c.sysfs_has("spectre_v2", "Retpolines"):
|
||||
ksft.test_result_skip("Skipping its_indirect_alignment.py: Retpolines deployed")
|
||||
ksft.finished()
|
||||
|
||||
c.check_dependencies_or_skip(['drgn', 'elftools', 'capstone'], script_name="its_indirect_alignment.py")
|
||||
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from drgn.helpers.common.memory import identify_address
|
||||
|
||||
cap = c.init_capstone()
|
||||
|
||||
if len(os.sys.argv) > 1:
|
||||
arg_vmlinux = os.sys.argv[1]
|
||||
if not os.path.exists(arg_vmlinux):
|
||||
ksft.test_result_fail(f"its_indirect_alignment.py: vmlinux not found at argument path: {arg_vmlinux}")
|
||||
ksft.exit_fail()
|
||||
os.makedirs(f"/usr/lib/debug/lib/modules/{os.uname().release}", exist_ok=True)
|
||||
os.system(f'cp {arg_vmlinux} /usr/lib/debug/lib/modules/$(uname -r)/vmlinux')
|
||||
|
||||
vmlinux = f"/usr/lib/debug/lib/modules/{os.uname().release}/vmlinux"
|
||||
if not os.path.exists(vmlinux):
|
||||
ksft.test_result_fail(f"its_indirect_alignment.py: vmlinux not found at {vmlinux}")
|
||||
ksft.exit_fail()
|
||||
|
||||
ksft.print_msg(f"Using vmlinux: {vmlinux}")
|
||||
|
||||
retpolines_start_vmlinux, retpolines_sec_offset, size = c.get_section_info(vmlinux, '.retpoline_sites')
|
||||
ksft.print_msg(f"vmlinux: Section .retpoline_sites (0x{retpolines_start_vmlinux:x}) found at 0x{retpolines_sec_offset:x} with size 0x{size:x}")
|
||||
|
||||
sites_offset = c.get_patch_sites(vmlinux, retpolines_sec_offset, size)
|
||||
total_retpoline_tests = len(sites_offset)
|
||||
ksft.print_msg(f"Found {total_retpoline_tests} retpoline sites")
|
||||
|
||||
prog = c.get_runtime_kernel()
|
||||
retpolines_start_kcore = prog.symbol('__retpoline_sites').address
|
||||
ksft.print_msg(f'kcore: __retpoline_sites: 0x{retpolines_start_kcore:x}')
|
||||
|
||||
x86_indirect_its_thunk_r15 = prog.symbol('__x86_indirect_its_thunk_r15').address
|
||||
ksft.print_msg(f'kcore: __x86_indirect_its_thunk_r15: 0x{x86_indirect_its_thunk_r15:x}')
|
||||
|
||||
tests_passed = 0
|
||||
tests_failed = 0
|
||||
tests_unknown = 0
|
||||
|
||||
with open(vmlinux, 'rb') as f:
|
||||
elffile = ELFFile(f)
|
||||
text_section = elffile.get_section_by_name('.text')
|
||||
|
||||
for i in range(0, len(sites_offset)):
|
||||
site = retpolines_start_kcore + sites_offset[i]
|
||||
vmlinux_site = retpolines_start_vmlinux + sites_offset[i]
|
||||
passed = unknown = failed = False
|
||||
try:
|
||||
vmlinux_insn = c.get_instruction_from_vmlinux(elffile, text_section, text_section['sh_addr'], vmlinux_site)
|
||||
kcore_insn = list(cap.disasm(prog.read(site, 16), site))[0]
|
||||
operand = kcore_insn.op_str
|
||||
insn_end = site + kcore_insn.size - 1 # TODO handle Jcc.32 __x86_indirect_thunk_\reg
|
||||
safe_site = insn_end & 0x20
|
||||
site_status = "" if safe_site else "(unsafe)"
|
||||
|
||||
ksft.print_msg(f"\nSite {i}: {identify_address(prog, site)} <0x{site:x}> {site_status}")
|
||||
ksft.print_msg(f"\tvmlinux: 0x{vmlinux_insn.address:x}:\t{vmlinux_insn.mnemonic}\t{vmlinux_insn.op_str}")
|
||||
ksft.print_msg(f"\tkcore: 0x{kcore_insn.address:x}:\t{kcore_insn.mnemonic}\t{kcore_insn.op_str}")
|
||||
|
||||
if (site & 0x20) ^ (insn_end & 0x20):
|
||||
ksft.print_msg(f"\tSite at safe/unsafe boundary: {str(kcore_insn.bytes)} {kcore_insn.mnemonic} {operand}")
|
||||
if safe_site:
|
||||
tests_passed += 1
|
||||
passed = True
|
||||
ksft.print_msg(f"\tPASSED: At safe address")
|
||||
continue
|
||||
|
||||
if operand.startswith('0xffffffff'):
|
||||
thunk = int(operand, 16)
|
||||
if thunk > x86_indirect_its_thunk_r15:
|
||||
insn_at_thunk = list(cap.disasm(prog.read(thunk, 16), thunk))[0]
|
||||
operand += ' -> ' + insn_at_thunk.mnemonic + ' ' + insn_at_thunk.op_str + ' <dynamic-thunk?>'
|
||||
if 'jmp' in insn_at_thunk.mnemonic and thunk & 0x20:
|
||||
ksft.print_msg(f"\tPASSED: Found {operand} at safe address")
|
||||
passed = True
|
||||
if not passed:
|
||||
if kcore_insn.operands[0].type == capstone.CS_OP_IMM:
|
||||
operand += ' <' + prog.symbol(int(operand, 16)) + '>'
|
||||
if '__x86_indirect_its_thunk_' in operand:
|
||||
ksft.print_msg(f"\tPASSED: Found {operand}")
|
||||
else:
|
||||
ksft.print_msg(f"\tPASSED: Found direct branch: {kcore_insn}, ITS thunk not required.")
|
||||
passed = True
|
||||
else:
|
||||
unknown = True
|
||||
if passed:
|
||||
tests_passed += 1
|
||||
elif unknown:
|
||||
ksft.print_msg(f"UNKNOWN: unexpected operand: {kcore_insn}")
|
||||
tests_unknown += 1
|
||||
else:
|
||||
ksft.print_msg(f'\t************* FAILED *************')
|
||||
ksft.print_msg(f"\tFound {kcore_insn.bytes} {kcore_insn.mnemonic} {operand}")
|
||||
ksft.print_msg(f'\t**********************************')
|
||||
tests_failed += 1
|
||||
except Exception as e:
|
||||
ksft.print_msg(f"UNKNOWN: An unexpected error occurred: {e}")
|
||||
tests_unknown += 1
|
||||
|
||||
ksft.print_msg(f"\n\nSummary:")
|
||||
ksft.print_msg(f"PASS: \t{tests_passed} \t/ {total_retpoline_tests}")
|
||||
ksft.print_msg(f"FAIL: \t{tests_failed} \t/ {total_retpoline_tests}")
|
||||
ksft.print_msg(f"UNKNOWN: \t{tests_unknown} \t/ {total_retpoline_tests}")
|
||||
|
||||
if tests_failed == 0:
|
||||
ksft.test_result_pass("All ITS return thunk sites passed")
|
||||
else:
|
||||
ksft.test_result_fail(f"{tests_failed} ITS return thunk sites failed")
|
||||
ksft.finished()
|
||||
109
tools/testing/selftests/x86/bugs/its_permutations.py
Normal file
109
tools/testing/selftests/x86/bugs/its_permutations.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Intel Corporation
|
||||
#
|
||||
# Test for indirect target selection (ITS) cmdline permutations with other bugs
|
||||
# like spectre_v2 and retbleed.
|
||||
|
||||
import os, sys, subprocess, itertools, re, shutil
|
||||
|
||||
test_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, test_dir + '/../../kselftest')
|
||||
import ksft
|
||||
import common as c
|
||||
|
||||
bug = "indirect_target_selection"
|
||||
mitigation = c.get_sysfs(bug)
|
||||
|
||||
if not mitigation or "Not affected" in mitigation:
|
||||
ksft.test_result_skip("Skipping its_permutations.py: not applicable")
|
||||
ksft.finished()
|
||||
|
||||
if shutil.which('vng') is None:
|
||||
ksft.test_result_skip("Skipping its_permutations.py: virtme-ng ('vng') not found in PATH.")
|
||||
ksft.finished()
|
||||
|
||||
TEST = f"{test_dir}/its_sysfs.py"
|
||||
default_kparam = ['clearcpuid=hypervisor', 'panic=5', 'panic_on_warn=1', 'oops=panic', 'nmi_watchdog=1', 'hung_task_panic=1']
|
||||
|
||||
DEBUG = " -v "
|
||||
|
||||
# Install dependencies
|
||||
# https://github.com/arighi/virtme-ng
|
||||
# apt install virtme-ng
|
||||
BOOT_CMD = f"vng --run {test_dir}/../../../../../arch/x86/boot/bzImage "
|
||||
#BOOT_CMD += DEBUG
|
||||
|
||||
bug = "indirect_target_selection"
|
||||
|
||||
input_options = {
|
||||
'indirect_target_selection' : ['off', 'on', 'stuff', 'vmexit'],
|
||||
'retbleed' : ['off', 'stuff', 'auto'],
|
||||
'spectre_v2' : ['off', 'on', 'eibrs', 'retpoline', 'ibrs', 'eibrs,retpoline'],
|
||||
}
|
||||
|
||||
def pretty_print(output):
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
|
||||
# Define patterns and their corresponding colors
|
||||
patterns = {
|
||||
r"^ok \d+": OKGREEN,
|
||||
r"^not ok \d+": FAIL,
|
||||
r"^# Testing .*": OKBLUE,
|
||||
r"^# Found: .*": WARNING,
|
||||
r"^# Totals: .*": BOLD,
|
||||
r"pass:([1-9]\d*)": OKGREEN,
|
||||
r"fail:([1-9]\d*)": FAIL,
|
||||
r"skip:([1-9]\d*)": WARNING,
|
||||
}
|
||||
|
||||
# Apply colors based on patterns
|
||||
for pattern, color in patterns.items():
|
||||
output = re.sub(pattern, lambda match: f"{color}{match.group(0)}{ENDC}", output, flags=re.MULTILINE)
|
||||
|
||||
print(output)
|
||||
|
||||
combinations = list(itertools.product(*input_options.values()))
|
||||
ksft.print_header()
|
||||
ksft.set_plan(len(combinations))
|
||||
|
||||
logs = ""
|
||||
|
||||
for combination in combinations:
|
||||
append = ""
|
||||
log = ""
|
||||
for p in default_kparam:
|
||||
append += f' --append={p}'
|
||||
command = BOOT_CMD + append
|
||||
test_params = ""
|
||||
for i, key in enumerate(input_options.keys()):
|
||||
param = f'{key}={combination[i]}'
|
||||
test_params += f' {param}'
|
||||
command += f" --append={param}"
|
||||
command += f" -- {TEST}"
|
||||
test_name = f"{bug} {test_params}"
|
||||
pretty_print(f'# Testing {test_name}')
|
||||
t = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
t.wait()
|
||||
output, _ = t.communicate()
|
||||
if t.returncode == 0:
|
||||
ksft.test_result_pass(test_name)
|
||||
else:
|
||||
ksft.test_result_fail(test_name)
|
||||
output = output.decode()
|
||||
log += f" {output}"
|
||||
pretty_print(log)
|
||||
logs += output + "\n"
|
||||
|
||||
# Optionally use tappy to parse the output
|
||||
# apt install python3-tappy
|
||||
with open("logs.txt", "w") as f:
|
||||
f.write(logs)
|
||||
|
||||
ksft.finished()
|
||||
139
tools/testing/selftests/x86/bugs/its_ret_alignment.py
Normal file
139
tools/testing/selftests/x86/bugs/its_ret_alignment.py
Normal file
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Intel Corporation
|
||||
#
|
||||
# Test for indirect target selection (ITS) mitigation.
|
||||
#
|
||||
# Tests if the RETs are correctly patched by evaluating the
|
||||
# vmlinux .return_sites in /proc/kcore.
|
||||
#
|
||||
# Install dependencies
|
||||
# add-apt-repository ppa:michel-slm/kernel-utils
|
||||
# apt update
|
||||
# apt install -y python3-drgn python3-pyelftools python3-capstone
|
||||
#
|
||||
# Run on target machine
|
||||
# mkdir -p /usr/lib/debug/lib/modules/$(uname -r)
|
||||
# cp $VMLINUX /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
|
||||
#
|
||||
# Usage: ./its_ret_alignment.py
|
||||
|
||||
import os, sys, argparse
|
||||
from pathlib import Path
|
||||
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, this_dir + '/../../kselftest')
|
||||
import ksft
|
||||
import common as c
|
||||
|
||||
bug = "indirect_target_selection"
|
||||
mitigation = c.get_sysfs(bug)
|
||||
if not mitigation or "Aligned branch/return thunks" not in mitigation:
|
||||
ksft.test_result_skip("Skipping its_ret_alignment.py: Aligned branch/return thunks not enabled")
|
||||
ksft.finished()
|
||||
|
||||
c.check_dependencies_or_skip(['drgn', 'elftools', 'capstone'], script_name="its_ret_alignment.py")
|
||||
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from drgn.helpers.common.memory import identify_address
|
||||
|
||||
cap = c.init_capstone()
|
||||
|
||||
if len(os.sys.argv) > 1:
|
||||
arg_vmlinux = os.sys.argv[1]
|
||||
if not os.path.exists(arg_vmlinux):
|
||||
ksft.test_result_fail(f"its_ret_alignment.py: vmlinux not found at user-supplied path: {arg_vmlinux}")
|
||||
ksft.exit_fail()
|
||||
os.makedirs(f"/usr/lib/debug/lib/modules/{os.uname().release}", exist_ok=True)
|
||||
os.system(f'cp {arg_vmlinux} /usr/lib/debug/lib/modules/$(uname -r)/vmlinux')
|
||||
|
||||
vmlinux = f"/usr/lib/debug/lib/modules/{os.uname().release}/vmlinux"
|
||||
if not os.path.exists(vmlinux):
|
||||
ksft.test_result_fail(f"its_ret_alignment.py: vmlinux not found at {vmlinux}")
|
||||
ksft.exit_fail()
|
||||
|
||||
ksft.print_msg(f"Using vmlinux: {vmlinux}")
|
||||
|
||||
rethunks_start_vmlinux, rethunks_sec_offset, size = c.get_section_info(vmlinux, '.return_sites')
|
||||
ksft.print_msg(f"vmlinux: Section .return_sites (0x{rethunks_start_vmlinux:x}) found at 0x{rethunks_sec_offset:x} with size 0x{size:x}")
|
||||
|
||||
sites_offset = c.get_patch_sites(vmlinux, rethunks_sec_offset, size)
|
||||
total_rethunk_tests = len(sites_offset)
|
||||
ksft.print_msg(f"Found {total_rethunk_tests} rethunk sites")
|
||||
|
||||
prog = c.get_runtime_kernel()
|
||||
rethunks_start_kcore = prog.symbol('__return_sites').address
|
||||
ksft.print_msg(f'kcore: __rethunk_sites: 0x{rethunks_start_kcore:x}')
|
||||
|
||||
its_return_thunk = prog.symbol('its_return_thunk').address
|
||||
ksft.print_msg(f'kcore: its_return_thunk: 0x{its_return_thunk:x}')
|
||||
|
||||
tests_passed = 0
|
||||
tests_failed = 0
|
||||
tests_unknown = 0
|
||||
tests_skipped = 0
|
||||
|
||||
with open(vmlinux, 'rb') as f:
|
||||
elffile = ELFFile(f)
|
||||
text_section = elffile.get_section_by_name('.text')
|
||||
|
||||
for i in range(len(sites_offset)):
|
||||
site = rethunks_start_kcore + sites_offset[i]
|
||||
vmlinux_site = rethunks_start_vmlinux + sites_offset[i]
|
||||
try:
|
||||
passed = unknown = failed = skipped = False
|
||||
|
||||
symbol = identify_address(prog, site)
|
||||
vmlinux_insn = c.get_instruction_from_vmlinux(elffile, text_section, text_section['sh_addr'], vmlinux_site)
|
||||
kcore_insn = list(cap.disasm(prog.read(site, 16), site))[0]
|
||||
|
||||
insn_end = site + kcore_insn.size - 1
|
||||
|
||||
safe_site = insn_end & 0x20
|
||||
site_status = "" if safe_site else "(unsafe)"
|
||||
|
||||
ksft.print_msg(f"\nSite {i}: {symbol} <0x{site:x}> {site_status}")
|
||||
ksft.print_msg(f"\tvmlinux: 0x{vmlinux_insn.address:x}:\t{vmlinux_insn.mnemonic}\t{vmlinux_insn.op_str}")
|
||||
ksft.print_msg(f"\tkcore: 0x{kcore_insn.address:x}:\t{kcore_insn.mnemonic}\t{kcore_insn.op_str}")
|
||||
|
||||
if safe_site:
|
||||
tests_passed += 1
|
||||
passed = True
|
||||
ksft.print_msg(f"\tPASSED: At safe address")
|
||||
continue
|
||||
|
||||
if "jmp" in kcore_insn.mnemonic:
|
||||
passed = True
|
||||
elif "ret" not in kcore_insn.mnemonic:
|
||||
skipped = True
|
||||
|
||||
if passed:
|
||||
ksft.print_msg(f"\tPASSED: Found {kcore_insn.mnemonic} {kcore_insn.op_str}")
|
||||
tests_passed += 1
|
||||
elif skipped:
|
||||
ksft.print_msg(f"\tSKIPPED: Found '{kcore_insn.mnemonic}'")
|
||||
tests_skipped += 1
|
||||
elif unknown:
|
||||
ksft.print_msg(f"UNKNOWN: An unknown instruction: {kcore_insn}")
|
||||
tests_unknown += 1
|
||||
else:
|
||||
ksft.print_msg(f'\t************* FAILED *************')
|
||||
ksft.print_msg(f"\tFound {kcore_insn.mnemonic} {kcore_insn.op_str}")
|
||||
ksft.print_msg(f'\t**********************************')
|
||||
tests_failed += 1
|
||||
except Exception as e:
|
||||
ksft.print_msg(f"UNKNOWN: An unexpected error occurred: {e}")
|
||||
tests_unknown += 1
|
||||
|
||||
ksft.print_msg(f"\n\nSummary:")
|
||||
ksft.print_msg(f"PASSED: \t{tests_passed} \t/ {total_rethunk_tests}")
|
||||
ksft.print_msg(f"FAILED: \t{tests_failed} \t/ {total_rethunk_tests}")
|
||||
ksft.print_msg(f"SKIPPED: \t{tests_skipped} \t/ {total_rethunk_tests}")
|
||||
ksft.print_msg(f"UNKNOWN: \t{tests_unknown} \t/ {total_rethunk_tests}")
|
||||
|
||||
if tests_failed == 0:
|
||||
ksft.test_result_pass("All ITS return thunk sites passed.")
|
||||
else:
|
||||
ksft.test_result_fail(f"{tests_failed} failed sites need ITS return thunks.")
|
||||
ksft.finished()
|
||||
65
tools/testing/selftests/x86/bugs/its_sysfs.py
Normal file
65
tools/testing/selftests/x86/bugs/its_sysfs.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Intel Corporation
|
||||
#
|
||||
# Test for Indirect Target Selection(ITS) mitigation sysfs status.
|
||||
|
||||
import sys, os, re
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, this_dir + '/../../kselftest')
|
||||
import ksft
|
||||
|
||||
from common import *
|
||||
|
||||
bug = "indirect_target_selection"
|
||||
mitigation = get_sysfs(bug)
|
||||
|
||||
ITS_MITIGATION_ALIGNED_THUNKS = "Mitigation: Aligned branch/return thunks"
|
||||
ITS_MITIGATION_RETPOLINE_STUFF = "Mitigation: Retpolines, Stuffing RSB"
|
||||
ITS_MITIGATION_VMEXIT_ONLY = "Mitigation: Vulnerable, KVM: Not affected"
|
||||
ITS_MITIGATION_VULNERABLE = "Vulnerable"
|
||||
|
||||
def check_mitigation():
|
||||
if mitigation == ITS_MITIGATION_ALIGNED_THUNKS:
|
||||
if cmdline_has(f'{bug}=stuff') and sysfs_has("spectre_v2", "Retpolines"):
|
||||
bug_check_fail(bug, ITS_MITIGATION_ALIGNED_THUNKS, ITS_MITIGATION_RETPOLINE_STUFF)
|
||||
return
|
||||
if cmdline_has(f'{bug}=vmexit') and cpuinfo_has('its_native_only'):
|
||||
bug_check_fail(bug, ITS_MITIGATION_ALIGNED_THUNKS, ITS_MITIGATION_VMEXIT_ONLY)
|
||||
return
|
||||
bug_check_pass(bug, ITS_MITIGATION_ALIGNED_THUNKS)
|
||||
return
|
||||
|
||||
if mitigation == ITS_MITIGATION_RETPOLINE_STUFF:
|
||||
if cmdline_has(f'{bug}=stuff') and sysfs_has("spectre_v2", "Retpolines"):
|
||||
bug_check_pass(bug, ITS_MITIGATION_RETPOLINE_STUFF)
|
||||
return
|
||||
if sysfs_has('retbleed', 'Stuffing'):
|
||||
bug_check_pass(bug, ITS_MITIGATION_RETPOLINE_STUFF)
|
||||
return
|
||||
bug_check_fail(bug, ITS_MITIGATION_RETPOLINE_STUFF, ITS_MITIGATION_ALIGNED_THUNKS)
|
||||
|
||||
if mitigation == ITS_MITIGATION_VMEXIT_ONLY:
|
||||
if cmdline_has(f'{bug}=vmexit') and cpuinfo_has('its_native_only'):
|
||||
bug_check_pass(bug, ITS_MITIGATION_VMEXIT_ONLY)
|
||||
return
|
||||
bug_check_fail(bug, ITS_MITIGATION_VMEXIT_ONLY, ITS_MITIGATION_ALIGNED_THUNKS)
|
||||
|
||||
if mitigation == ITS_MITIGATION_VULNERABLE:
|
||||
if sysfs_has("spectre_v2", "Vulnerable"):
|
||||
bug_check_pass(bug, ITS_MITIGATION_VULNERABLE)
|
||||
else:
|
||||
bug_check_fail(bug, "Mitigation", ITS_MITIGATION_VULNERABLE)
|
||||
|
||||
bug_status_unknown(bug, mitigation)
|
||||
return
|
||||
|
||||
ksft.print_header()
|
||||
ksft.set_plan(1)
|
||||
ksft.print_msg(f'{bug}: {mitigation} ...')
|
||||
|
||||
if not basic_checks_sufficient(bug, mitigation):
|
||||
check_mitigation()
|
||||
|
||||
ksft.finished()
|
||||
Reference in New Issue
Block a user