如何在禁用 SMP 支持的情况下使用 运行 "invd" 指令?
How to run "invd" instruction with disabled SMP support?
我正在尝试从内核模块执行“invd”指令。我之前问过一个类似的问题 How to execute “invd” instruction? 和@Peter Cordes 的回答,我知道我不能在系统启动后安全地 运行 SMP 系统上的这条指令。那么,在没有 SMP 支持的情况下,我不能在启动后 运行 这条指令吗?因为没有其他核心运行ning,所以内存不一致没有变化?我用 -o0
标志编译了以下内核模块,
static int __init deviceDriver_init(void){
unsigned long flags;
int LEN=10;
int STEP=1;
int VALUE=1;
int arr[LEN];
int i;
unsigned long dummy;
printk(KERN_INFO "invd Driver loaded\n");
//wbinvd();
//asm volatile("cpuid\n":::);
local_irq_disable();
__asm__ __volatile__(
"wbinvd\n"
"loop:"
"movq %%rdx, (%%rbx);"
"leaq (%%rbx, %%rcx, 8), %%rbx;"
"cmpq %%rbx, %%rax;"
"jg loop;"
"invd\n"
: "=b"(dummy) // output
: "b" (arr),
"a" (arr+LEN),
"c" (STEP),
"d" (VALUE)
: "cc", "memory"
);
local_irq_enable();
//asm volatile("invd\n":::);
printk(KERN_INFO "invd execute\n");
return 0;
}
我在终端中插入我得到的模块时仍然收到以下错误 Segmentation fault (core dumped)
并且 dmesg 显示,
[ 2590.518614] invd Driver loaded
[ 2590.518840] general protection fault: 0000 [#5] SMP PTI
我已经用 nosmp
启动我的内核,但我不明白为什么 dmesg
仍然显示 SMP PTI
$cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.15.0-136-generic root=UUID=dbe747ff-a6a5-45cb-8553-c6db6d445d3d ro quiet splash nosmp vt.handoff=7
更新post:
正如我在评论部分提到的,从 BIOS 禁用 SGX 后,我能够 运行 这个 invd
而没有任何错误。但是,当我尝试在具有相同内核版本的另一台机器上 运行 相同的代码时,我仍然会收到相同的错误消息。这很奇怪,我无法解释为什么会这样。正如在评论部分中,@prl 提到错误可能来自 invd
之后的指令。我开始认为也许那是真的。因为 dmesg
中倒数第二行在 RED [ 153.527386] RIP: loop+0xc/0xf22 [noSmp8] RSP: ffffb8d9450a7be0
中突出显示。所以,似乎错误来自 loop
内部。我已经按照建议更新了__init函数代码。我不擅长汇编代码,谁能告诉我内联汇编代码是否正确?如果此内联汇编代码不正确,如何修复代码?我的整个 dmesg
痕迹是,
[ 153.514293] invd Driver loaded
[ 153.514547] general protection fault: 0000 [#1] SMP PTI
[ 153.514656] Modules linked in: noSmp8(OE+) xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack libcrc32c ipt_REJECT nf_reject_ipv4 xt_tcpudp bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter ip_tables x_tables ccm arc4 intel_rapl rt2800usb rt2x00usb x86_pkg_temp_thermal intel_powerclamp rt2800lib coretemp rt2x00lib mac80211 cfg80211 kvm_intel kvm irqbypass snd_hda_codec_realtek crct10dif_pclmul crc32_pclmul ghash_clmulni_intel snd_hda_codec_hdmi pcbc aesni_intel aes_x86_64 crypto_simd glue_helper cryptd intel_cstate intel_rapl_perf dell_smm_hwmon dell_wmi dell_smbios dcdbas intel_wmi_thunderbolt snd_hda_codec_generic dell_wmi_descriptor wmi_bmof snd_seq_midi snd_seq_midi_event
[ 153.515454] serio_raw snd_hda_intel snd_hda_codec snd_hda_core sparse_keymap snd_hwdep snd_rawmidi joydev input_leds snd_seq snd_pcm snd_seq_device snd_timer snd soundcore mei_me mei shpchp intel_pch_thermal mac_hid acpi_pad parport_pc ppdev lp parport autofs4 hid_generic usbhid hid nouveau mxm_wmi ttm drm_kms_helper psmouse syscopyarea sysfillrect sysimgblt igb e1000e dca i2c_algo_bit ptp pps_core ahci libahci fb_sys_fops drm wmi video
[ 153.516038] CPU: 0 PID: 4024 Comm: insmod Tainted: G OE 4.15.0-136-generic #140~16.04.1-Ubuntu
[ 153.516331] Hardware name: Dell Inc. BIOS 1.3.2 01/25/2016
[ 153.516626] RIP: 0010:loop+0xc/0xf22 [noSmp8]
[ 153.516917] RSP: 0018:ffffb8d9450a7be0 EFLAGS: 00010046
[ 153.517213] RAX: ffffb8d9450a7c08 RBX: ffffb8d9450a7c08 RCX: 0000000000000001
[ 153.517513] RDX: 0000000000000001 RSI: ffffb8d9450a7be0 RDI: ffff8edaadc16490
[ 153.517814] RBP: ffffb8d9450a7c60 R08: 0000000000012c40 R09: ffffffffb39624c4
[ 153.518119] R10: ffffb8d9450a7c78 R11: 000000000000038c R12: ffffb8d9450a7c10
[ 153.518427] R13: 0000000000000000 R14: 0000000000000001 R15: ffff8eda4c6bd660
[ 153.518730] FS: 00007fd7f09cf700(0000) GS:ffff8edaadc00000(0000) knlGS:0000000000000000
[ 153.519036] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 153.519346] CR2: 00005634f95fde50 CR3: 000000040dd2c001 CR4: 00000000003606f0
[ 153.519656] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 153.519980] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 153.520289] Call Trace:
[ 153.520597] ? 0xffffffffc050d000
[ 153.520899] do_one_initcall+0x55/0x1ac
[ 153.521201] ? do_one_initcall+0x55/0x1ac
[ 153.521504] ? do_init_module+0x27/0x223
[ 153.521808] ? _cond_resched+0x32/0x50
[ 153.522107] ? kmem_cache_alloc_trace+0x165/0x1c0
[ 153.522408] do_init_module+0x5f/0x223
[ 153.522710] load_module+0x188c/0x1ea0
[ 153.523016] ? ima_post_read_file+0x83/0xa0
[ 153.523320] SYSC_finit_module+0xe5/0x120
[ 153.523623] ? SYSC_finit_module+0xe5/0x120
[ 153.523927] SyS_finit_module+0xe/0x10
[ 153.524231] do_syscall_64+0x73/0x130
[ 153.524534] entry_SYSCALL_64_after_hwframe+0x41/0xa6
[ 153.524838] RIP: 0033:0x7fd7f04fd599
[ 153.525144] RSP: 002b:00007ffda61c2968 EFLAGS: 00000202 ORIG_RAX: 0000000000000139
[ 153.525455] RAX: ffffffffffffffda RBX: 00005643631d7210 RCX: 00007fd7f04fd599
[ 153.525768] RDX: 0000000000000000 RSI: 0000564361c3226b RDI: 0000000000000003
[ 153.526084] RBP: 0000564361c3226b R08: 0000000000000000 R09: 00007fd7f07c2ea0
[ 153.526403] R10: 0000000000000003 R11: 0000000000000202 R12: 0000000000000000
[ 153.526722] R13: 00005643631d7ca0 R14: 0000000000000000 R15: 0000000000000000
[ 153.527040] Code: 00 48 8b 75 c8 48 8b 45 c8 8b 55 b8 48 63 d2 48 c1 e2 02 48 01 d0 8b 4d b4 8b 55 bc 48 89 f3 48 89 13 48 8d 1c cb 48 39 d8 7f f4 <0f> 08 48 89 d8 48 89 45 d0 e8 40 ef 73 00 48 c7 c7 c7 d0 c4 c0
[ 153.527386] RIP: loop+0xc/0xf22 [noSmp8] RSP: ffffb8d9450a7be0
[ 153.530228] ---[ end trace cc9ea64985c9fe34 ]---
所以,即使没有 SMP 也无法 运行 invd
?
这里有 2 个问题:
a) 如何执行 INVD(不安全)
为此,您需要 运行 在 CPL=0,并且您必须确保 CPU 没有使用英特尔的任何“处理器保留内存保护” Software Guard Extensions(允许程序拥有 OS 无法篡改的 shielded/private/encrypted space 的扩展,通常用于数字版权管理方案,但可能用于增强 security/confidentiality 其他东西)。
请注意,Linux 的最新版本支持 SGX,但我不确定何时引入支持或您的内核有多旧,或者它是否 enabled/disabled。
如果其中任何一个不正确(例如,您处于 CPL=3 或存在“处理器保留内存保护”),您将获得一般保护错误异常。
b) 如何安全地执行 INVD
为此,您必须确保缓存(包括“外部缓存”——例如可能包括 eDRAM 和内置于非易失性 RAM 中的缓存)不包含任何会导致问题的修改数据如果丢失。这包括来自以下方面的数据:
IRQ。这些可以被禁用。
NMI 和机器检查异常。对于 运行 OS 来说,几乎不可能 stop/disable 这些,如果你可以禁用它们,那么它就像祈祷你的手指同时忽略关键的硬件故障(一个非常糟糕的主意)。
固件的系统管理模式。这是一种特殊的 CPU 模式,固件用于各种不受 OS/kernel 控制的事情(例如 ECC 清理、一些电源管理、遗留设备的模拟)。无法禁用。
由 CPU 本身完成的写入。这包括更新页表中的 accessed/dirty 标志(无法禁用),以及在内存中存储数据的任何性能监控或调试功能(可以“未启用”)。
由于这些限制(并且不要忘记性能问题),只有 2 种情况下 INVD 可能是正常的 - 需要确定 RAM 芯片大小和配置内存控制器的早期固件代码(很可能是 useful/sane),以及计算机关闭前的瞬间(这可能毫无意义)。
猜测
我猜测(基于我无法想到任何其他合理的原因)您想要构建临时 shielded/private 内存区域(以增强安全性 - 例如,以便您放入的数据区域不会t/can 泄漏到 RAM 中)。在这种情况下(具有讽刺意味的是)专为这项工作设计的工具 (SGX) 可能会阻止你做得不好。
我正在尝试从内核模块执行“invd”指令。我之前问过一个类似的问题 How to execute “invd” instruction? 和@Peter Cordes 的回答,我知道我不能在系统启动后安全地 运行 SMP 系统上的这条指令。那么,在没有 SMP 支持的情况下,我不能在启动后 运行 这条指令吗?因为没有其他核心运行ning,所以内存不一致没有变化?我用 -o0
标志编译了以下内核模块,
static int __init deviceDriver_init(void){
unsigned long flags;
int LEN=10;
int STEP=1;
int VALUE=1;
int arr[LEN];
int i;
unsigned long dummy;
printk(KERN_INFO "invd Driver loaded\n");
//wbinvd();
//asm volatile("cpuid\n":::);
local_irq_disable();
__asm__ __volatile__(
"wbinvd\n"
"loop:"
"movq %%rdx, (%%rbx);"
"leaq (%%rbx, %%rcx, 8), %%rbx;"
"cmpq %%rbx, %%rax;"
"jg loop;"
"invd\n"
: "=b"(dummy) // output
: "b" (arr),
"a" (arr+LEN),
"c" (STEP),
"d" (VALUE)
: "cc", "memory"
);
local_irq_enable();
//asm volatile("invd\n":::);
printk(KERN_INFO "invd execute\n");
return 0;
}
我在终端中插入我得到的模块时仍然收到以下错误 Segmentation fault (core dumped)
并且 dmesg 显示,
[ 2590.518614] invd Driver loaded
[ 2590.518840] general protection fault: 0000 [#5] SMP PTI
我已经用 nosmp
启动我的内核,但我不明白为什么 dmesg
仍然显示 SMP PTI
$cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.15.0-136-generic root=UUID=dbe747ff-a6a5-45cb-8553-c6db6d445d3d ro quiet splash nosmp vt.handoff=7
更新post:
正如我在评论部分提到的,从 BIOS 禁用 SGX 后,我能够 运行 这个 invd
而没有任何错误。但是,当我尝试在具有相同内核版本的另一台机器上 运行 相同的代码时,我仍然会收到相同的错误消息。这很奇怪,我无法解释为什么会这样。正如在评论部分中,@prl 提到错误可能来自 invd
之后的指令。我开始认为也许那是真的。因为 dmesg
中倒数第二行在 RED [ 153.527386] RIP: loop+0xc/0xf22 [noSmp8] RSP: ffffb8d9450a7be0
中突出显示。所以,似乎错误来自 loop
内部。我已经按照建议更新了__init函数代码。我不擅长汇编代码,谁能告诉我内联汇编代码是否正确?如果此内联汇编代码不正确,如何修复代码?我的整个 dmesg
痕迹是,
[ 153.514293] invd Driver loaded
[ 153.514547] general protection fault: 0000 [#1] SMP PTI
[ 153.514656] Modules linked in: noSmp8(OE+) xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack libcrc32c ipt_REJECT nf_reject_ipv4 xt_tcpudp bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter ip_tables x_tables ccm arc4 intel_rapl rt2800usb rt2x00usb x86_pkg_temp_thermal intel_powerclamp rt2800lib coretemp rt2x00lib mac80211 cfg80211 kvm_intel kvm irqbypass snd_hda_codec_realtek crct10dif_pclmul crc32_pclmul ghash_clmulni_intel snd_hda_codec_hdmi pcbc aesni_intel aes_x86_64 crypto_simd glue_helper cryptd intel_cstate intel_rapl_perf dell_smm_hwmon dell_wmi dell_smbios dcdbas intel_wmi_thunderbolt snd_hda_codec_generic dell_wmi_descriptor wmi_bmof snd_seq_midi snd_seq_midi_event
[ 153.515454] serio_raw snd_hda_intel snd_hda_codec snd_hda_core sparse_keymap snd_hwdep snd_rawmidi joydev input_leds snd_seq snd_pcm snd_seq_device snd_timer snd soundcore mei_me mei shpchp intel_pch_thermal mac_hid acpi_pad parport_pc ppdev lp parport autofs4 hid_generic usbhid hid nouveau mxm_wmi ttm drm_kms_helper psmouse syscopyarea sysfillrect sysimgblt igb e1000e dca i2c_algo_bit ptp pps_core ahci libahci fb_sys_fops drm wmi video
[ 153.516038] CPU: 0 PID: 4024 Comm: insmod Tainted: G OE 4.15.0-136-generic #140~16.04.1-Ubuntu
[ 153.516331] Hardware name: Dell Inc. BIOS 1.3.2 01/25/2016
[ 153.516626] RIP: 0010:loop+0xc/0xf22 [noSmp8]
[ 153.516917] RSP: 0018:ffffb8d9450a7be0 EFLAGS: 00010046
[ 153.517213] RAX: ffffb8d9450a7c08 RBX: ffffb8d9450a7c08 RCX: 0000000000000001
[ 153.517513] RDX: 0000000000000001 RSI: ffffb8d9450a7be0 RDI: ffff8edaadc16490
[ 153.517814] RBP: ffffb8d9450a7c60 R08: 0000000000012c40 R09: ffffffffb39624c4
[ 153.518119] R10: ffffb8d9450a7c78 R11: 000000000000038c R12: ffffb8d9450a7c10
[ 153.518427] R13: 0000000000000000 R14: 0000000000000001 R15: ffff8eda4c6bd660
[ 153.518730] FS: 00007fd7f09cf700(0000) GS:ffff8edaadc00000(0000) knlGS:0000000000000000
[ 153.519036] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 153.519346] CR2: 00005634f95fde50 CR3: 000000040dd2c001 CR4: 00000000003606f0
[ 153.519656] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 153.519980] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 153.520289] Call Trace:
[ 153.520597] ? 0xffffffffc050d000
[ 153.520899] do_one_initcall+0x55/0x1ac
[ 153.521201] ? do_one_initcall+0x55/0x1ac
[ 153.521504] ? do_init_module+0x27/0x223
[ 153.521808] ? _cond_resched+0x32/0x50
[ 153.522107] ? kmem_cache_alloc_trace+0x165/0x1c0
[ 153.522408] do_init_module+0x5f/0x223
[ 153.522710] load_module+0x188c/0x1ea0
[ 153.523016] ? ima_post_read_file+0x83/0xa0
[ 153.523320] SYSC_finit_module+0xe5/0x120
[ 153.523623] ? SYSC_finit_module+0xe5/0x120
[ 153.523927] SyS_finit_module+0xe/0x10
[ 153.524231] do_syscall_64+0x73/0x130
[ 153.524534] entry_SYSCALL_64_after_hwframe+0x41/0xa6
[ 153.524838] RIP: 0033:0x7fd7f04fd599
[ 153.525144] RSP: 002b:00007ffda61c2968 EFLAGS: 00000202 ORIG_RAX: 0000000000000139
[ 153.525455] RAX: ffffffffffffffda RBX: 00005643631d7210 RCX: 00007fd7f04fd599
[ 153.525768] RDX: 0000000000000000 RSI: 0000564361c3226b RDI: 0000000000000003
[ 153.526084] RBP: 0000564361c3226b R08: 0000000000000000 R09: 00007fd7f07c2ea0
[ 153.526403] R10: 0000000000000003 R11: 0000000000000202 R12: 0000000000000000
[ 153.526722] R13: 00005643631d7ca0 R14: 0000000000000000 R15: 0000000000000000
[ 153.527040] Code: 00 48 8b 75 c8 48 8b 45 c8 8b 55 b8 48 63 d2 48 c1 e2 02 48 01 d0 8b 4d b4 8b 55 bc 48 89 f3 48 89 13 48 8d 1c cb 48 39 d8 7f f4 <0f> 08 48 89 d8 48 89 45 d0 e8 40 ef 73 00 48 c7 c7 c7 d0 c4 c0
[ 153.527386] RIP: loop+0xc/0xf22 [noSmp8] RSP: ffffb8d9450a7be0
[ 153.530228] ---[ end trace cc9ea64985c9fe34 ]---
所以,即使没有 SMP 也无法 运行 invd
?
这里有 2 个问题:
a) 如何执行 INVD(不安全)
为此,您需要 运行 在 CPL=0,并且您必须确保 CPU 没有使用英特尔的任何“处理器保留内存保护” Software Guard Extensions(允许程序拥有 OS 无法篡改的 shielded/private/encrypted space 的扩展,通常用于数字版权管理方案,但可能用于增强 security/confidentiality 其他东西)。
请注意,Linux 的最新版本支持 SGX,但我不确定何时引入支持或您的内核有多旧,或者它是否 enabled/disabled。
如果其中任何一个不正确(例如,您处于 CPL=3 或存在“处理器保留内存保护”),您将获得一般保护错误异常。
b) 如何安全地执行 INVD
为此,您必须确保缓存(包括“外部缓存”——例如可能包括 eDRAM 和内置于非易失性 RAM 中的缓存)不包含任何会导致问题的修改数据如果丢失。这包括来自以下方面的数据:
IRQ。这些可以被禁用。
NMI 和机器检查异常。对于 运行 OS 来说,几乎不可能 stop/disable 这些,如果你可以禁用它们,那么它就像祈祷你的手指同时忽略关键的硬件故障(一个非常糟糕的主意)。
固件的系统管理模式。这是一种特殊的 CPU 模式,固件用于各种不受 OS/kernel 控制的事情(例如 ECC 清理、一些电源管理、遗留设备的模拟)。无法禁用。
由 CPU 本身完成的写入。这包括更新页表中的 accessed/dirty 标志(无法禁用),以及在内存中存储数据的任何性能监控或调试功能(可以“未启用”)。
由于这些限制(并且不要忘记性能问题),只有 2 种情况下 INVD 可能是正常的 - 需要确定 RAM 芯片大小和配置内存控制器的早期固件代码(很可能是 useful/sane),以及计算机关闭前的瞬间(这可能毫无意义)。
猜测
我猜测(基于我无法想到任何其他合理的原因)您想要构建临时 shielded/private 内存区域(以增强安全性 - 例如,以便您放入的数据区域不会t/can 泄漏到 RAM 中)。在这种情况下(具有讽刺意味的是)专为这项工作设计的工具 (SGX) 可能会阻止你做得不好。