windows: use RtlQueryRegistryValues to query reg in a single syscall

This commit is contained in:
Jakub Konka 2022-11-26 17:34:45 +01:00
parent 153afed877
commit 7ea2c7fbcd
4 changed files with 261 additions and 186 deletions

View File

@ -2088,6 +2088,7 @@ pub const LPWSTR = [*:0]WCHAR;
pub const LPCWSTR = [*:0]const WCHAR;
pub const PVOID = *anyopaque;
pub const PWSTR = [*:0]WCHAR;
pub const PCWSTR = [*:0]const WCHAR;
pub const SIZE_T = usize;
pub const UINT = c_uint;
pub const ULONG_PTR = usize;
@ -2876,8 +2877,134 @@ pub const ACCESS_MASK = DWORD;
pub const LSTATUS = LONG;
pub const HKEY = HANDLE;
pub const HKEY_LOCAL_MACHINE: HKEY = @intToPtr(HKEY, 0x80000002);
/// Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
/// KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights.
pub const KEY_ALL_ACCESS = 0xF003F;
/// Reserved for system use.
pub const KEY_CREATE_LINK = 0x0020;
/// Required to create a subkey of a registry key.
pub const KEY_CREATE_SUB_KEY = 0x0004;
/// Required to enumerate the subkeys of a registry key.
pub const KEY_ENUMERATE_SUB_KEYS = 0x0008;
/// Equivalent to KEY_READ.
pub const KEY_EXECUTE = 0x20019;
/// Required to request change notifications for a registry key or for subkeys of a registry key.
pub const KEY_NOTIFY = 0x0010;
/// Required to query the values of a registry key.
pub const KEY_QUERY_VALUE = 0x0001;
/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
pub const KEY_READ = 0x20019;
/// Required to create, delete, or set a registry value.
pub const KEY_SET_VALUE = 0x0002;
/// Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
/// This flag is ignored by 32-bit Windows.
pub const KEY_WOW64_32KEY = 0x0200;
/// Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
/// This flag is ignored by 32-bit Windows.
pub const KEY_WOW64_64KEY = 0x0100;
/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
pub const KEY_WRITE = 0x20006;
/// Open symbolic link.
pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
Flags: ULONG,
Name: ?PWSTR,
EntryContext: ?*anyopaque,
DefaultType: ULONG,
DefaultData: ?*anyopaque,
DefaultLength: ULONG,
};
pub const RTL_QUERY_REGISTRY_ROUTINE = ?std.meta.FnPtr(fn (
PWSTR,
ULONG,
?*anyopaque,
ULONG,
?*anyopaque,
?*anyopaque,
) callconv(WINAPI) NTSTATUS);
/// Path is a full path
pub const RTL_REGISTRY_ABSOLUTE = 0;
/// \Registry\Machine\System\CurrentControlSet\Services
pub const RTL_REGISTRY_SERVICES = 1;
/// \Registry\Machine\System\CurrentControlSet\Control
pub const RTL_REGISTRY_CONTROL = 2;
/// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
pub const RTL_REGISTRY_WINDOWS_NT = 3;
/// \Registry\Machine\Hardware\DeviceMap
pub const RTL_REGISTRY_DEVICEMAP = 4;
/// \Registry\User\CurrentUser
pub const RTL_REGISTRY_USER = 5;
pub const RTL_REGISTRY_MAXIMUM = 6;
/// Low order bits are registry handle
pub const RTL_REGISTRY_HANDLE = 0x40000000;
/// Indicates the key node is optional
pub const RTL_REGISTRY_OPTIONAL = 0x80000000;
/// Name is a subkey and remainder of table or until next subkey are value
/// names for that subkey to look at.
pub const RTL_QUERY_REGISTRY_SUBKEY = 0x00000001;
/// Reset current key to original key for this and all following table entries.
pub const RTL_QUERY_REGISTRY_TOPKEY = 0x00000002;
/// Fail if no match found for this table entry.
pub const RTL_QUERY_REGISTRY_REQUIRED = 0x00000004;
/// Used to mark a table entry that has no value name, just wants a call out, not
/// an enumeration of all values.
pub const RTL_QUERY_REGISTRY_NOVALUE = 0x00000008;
/// Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
/// to prevent the expansion of environment variable values in REG_EXPAND_SZ.
pub const RTL_QUERY_REGISTRY_NOEXPAND = 0x00000010;
/// QueryRoutine field ignored. EntryContext field points to location to store value.
/// For null terminated strings, EntryContext points to UNICODE_STRING structure that
/// that describes maximum size of buffer. If .Buffer field is NULL then a buffer is
/// allocated.
pub const RTL_QUERY_REGISTRY_DIRECT = 0x00000020;
/// Used to delete value keys after they are queried.
pub const RTL_QUERY_REGISTRY_DELETE = 0x00000040;
/// Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
/// of the stored registry value matches the type expected by the caller.
/// If the types do not match, the call fails.
pub const RTL_QUERY_REGISTRY_TYPECHECK = 0x00000100;
/// No value type
pub const REG_NONE = 0;
/// Unicode nul terminated string
pub const REG_SZ = 1;
/// Unicode nul terminated string (with environment variable references)
pub const REG_EXPAND_SZ = 2;
/// Free form binary
pub const REG_BINARY = 3;
/// 32-bit number
pub const REG_DWORD = 4;
/// 32-bit number (same as REG_DWORD)
pub const REG_DWORD_LITTLE_ENDIAN = 4;
/// 32-bit number
pub const REG_DWORD_BIG_ENDIAN = 5;
/// Symbolic Link (unicode)
pub const REG_LINK = 6;
/// Multiple Unicode strings
pub const REG_MULTI_SZ = 7;
/// Resource list in the resource map
pub const REG_RESOURCE_LIST = 8;
/// Resource list in the hardware description
pub const REG_FULL_RESOURCE_DESCRIPTOR = 9;
pub const REG_RESOURCE_REQUIREMENTS_LIST = 10;
pub const FILE_NOTIFY_INFORMATION = extern struct {
NextEntryOffset: DWORD,
Action: DWORD,
@ -4020,187 +4147,3 @@ pub fn IsProcessorFeaturePresent(feature: PF) bool {
if (@enumToInt(feature) >= PROCESSOR_FEATURE_MAX) return false;
return SharedUserData.ProcessorFeatures[@enumToInt(feature)] == 1;
}
pub const KEY_QUERY_VALUE = 0x0001;
/// Open symbolic link.
pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
inline fn IsPredefKey(hkey: HKEY) bool {
return @ptrToInt(hkey) & 0xF0000000 == 0x80000000;
}
inline fn GetPredefKeyIndex(hkey: HKEY) usize {
return @ptrToInt(hkey) & 0x0FFFFFFF;
}
inline fn ClosePredefKey(hkey: HKEY) void {
if (@ptrToInt(hkey) & 0x1 != 0) {
assert(ntdll.NtClose(hkey) == .SUCCESS);
}
}
const MAX_DEFAULT_HANDLES = 6;
pub const REG_MAX_NAME_SIZE = 256;
pub const RegOpenKeyOpts = struct {
ulOptions: DWORD = 0,
samDesired: ACCESS_MASK = KEY_QUERY_VALUE,
};
/// Pulls existing key from the registry.
pub fn RegOpenKey(hkey: HKEY, lpSubKey: []const u16, opts: RegOpenKeyOpts) !HKEY {
if (IsPredefKey(hkey) and lpSubKey.len == 0) {
return hkey;
}
const key_handle = try MapDefaultKey(hkey);
defer ClosePredefKey(key_handle);
var subkey_string: UNICODE_STRING = undefined;
if (lpSubKey.len == 0 or mem.eql(u16, &[_]u16{'\\'}, lpSubKey)) {
subkey_string = .{
.Length = 0,
.MaximumLength = 0,
.Buffer = @intToPtr([*]u16, @ptrToInt(&[0]u16{})),
};
} else {
const len_bytes = math.cast(u16, lpSubKey.len * 2) orelse return error.NameTooLong;
subkey_string = .{
.Length = len_bytes,
.MaximumLength = len_bytes,
.Buffer = @intToPtr([*]u16, @ptrToInt(lpSubKey.ptr)),
};
}
var attributes: ULONG = OBJ_CASE_INSENSITIVE;
if (opts.ulOptions & REG_OPTION_OPEN_LINK != 0) {
attributes |= OBJ_OPENLINK;
}
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = key_handle,
.Attributes = attributes,
.ObjectName = &subkey_string,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var result: HKEY = undefined;
const rc = ntdll.NtOpenKey(
&result,
opts.samDesired,
attr,
);
switch (rc) {
.SUCCESS => return result,
else => return unexpectedStatus(rc),
}
}
pub fn RegCloseKey(hkey: HKEY) void {
if (IsPredefKey(hkey)) return;
assert(ntdll.NtClose(hkey) == .SUCCESS);
}
extern var DefaultHandleHKUDisabled: BOOLEAN;
extern var DefaultHandlesDisabled: BOOLEAN;
extern var DefaultHandleTable: [MAX_DEFAULT_HANDLES]?HANDLE;
fn MapDefaultKey(key: HKEY) !HANDLE {
if (!IsPredefKey(key)) return @intToPtr(HANDLE, @ptrToInt(key) & ~@as(usize, 0x1));
const index = GetPredefKeyIndex(key);
if (index >= MAX_DEFAULT_HANDLES) {
return error.InvalidParameter;
}
const def_disabled = if (key == HKEY_LOCAL_MACHINE) DefaultHandleHKUDisabled else DefaultHandlesDisabled;
var handle: HANDLE = undefined;
var do_open: bool = true;
if (def_disabled != 0) {
const tmp = DefaultHandleTable[index];
if (tmp) |h| {
do_open = false;
handle = h;
}
}
if (do_open) {
handle = try OpenPredefinedKey(index);
}
if (def_disabled == 0) {
handle = @intToPtr(HANDLE, @ptrToInt(handle) | 0x1);
}
return handle;
}
fn OpenPredefinedKey(index: usize) !HANDLE {
switch (index) {
0 => {
// HKEY_CLASSES_ROOT
return error.Unimplemented;
},
1 => {
// HKEY_CURRENT_USER
return error.Unimplemented;
},
2 => {
// HKEY_LOCAL_MACHINE
return OpenLocalMachineKey();
},
3 => {
// HKEY_USERS
return error.Unimplemented;
},
5 => {
// HKEY_CURRENT_CONFIG
return error.Unimplemented;
},
6 => {
// HKEY_DYN_DATA
return error.Unimplemented;
},
else => {
return error.InvalidParameter;
},
}
}
fn OpenLocalMachineKey() !HANDLE {
const path = "\\Registry\\Machine";
var path_u16: [REG_MAX_NAME_SIZE]u16 = undefined;
const path_len_u16 = try std.unicode.utf8ToUtf16Le(&path_u16, path);
const path_len_bytes = @intCast(u16, path_len_u16 * 2);
var key_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @intToPtr([*]u16, @ptrToInt(&path_u16)),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = null,
.Attributes = OBJ_CASE_INSENSITIVE,
.ObjectName = &key_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var result: HKEY = undefined;
const rc = ntdll.NtOpenKey(
&result,
MAXIMUM_ALLOWED,
attr,
);
switch (rc) {
.SUCCESS => return result,
else => return unexpectedStatus(rc),
}
}

View File

@ -10,6 +10,7 @@ const DWORD = windows.DWORD;
const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS;
const HANDLE = windows.HANDLE;
const HMODULE = windows.HMODULE;
const HKEY = windows.HKEY;
const HRESULT = windows.HRESULT;
const LARGE_INTEGER = windows.LARGE_INTEGER;
const LPCWSTR = windows.LPCWSTR;
@ -57,6 +58,8 @@ const UCHAR = windows.UCHAR;
const FARPROC = windows.FARPROC;
const INIT_ONCE_FN = windows.INIT_ONCE_FN;
const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION;
const REGSAM = windows.REGSAM;
const LSTATUS = windows.LSTATUS;
pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
@ -412,3 +415,11 @@ pub extern "kernel32" fn SleepConditionVariableSRW(
pub extern "kernel32" fn TryAcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) BOOLEAN;
pub extern "kernel32" fn AcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
pub extern "kernel32" fn ReleaseSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
pub extern "kernel32" fn RegOpenKeyExW(
hkey: HKEY,
lpSubKey: LPCWSTR,
ulOptions: DWORD,
samDesired: REGSAM,
phkResult: *HANDLE,
) callconv(WINAPI) LSTATUS;

View File

@ -22,6 +22,8 @@ const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW;
const FILE_BASIC_INFORMATION = windows.FILE_BASIC_INFORMATION;
const SIZE_T = windows.SIZE_T;
const CURDIR = windows.CURDIR;
const PCWSTR = windows.PCWSTR;
const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE;
pub const THREADINFOCLASS = enum(c_int) {
ThreadBasicInformation,
@ -259,3 +261,11 @@ pub extern "ntdll" fn NtOpenKey(
DesiredAccess: ACCESS_MASK,
ObjectAttributes: OBJECT_ATTRIBUTES,
) callconv(WINAPI) NTSTATUS;
pub extern "ntdll" fn RtlQueryRegistryValues(
RelativeTo: ULONG,
Path: PCWSTR,
QueryTable: [*]RTL_QUERY_REGISTRY_TABLE,
Context: ?*anyopaque,
Environment: ?*anyopaque,
) callconv(WINAPI) NTSTATUS;

View File

@ -43,13 +43,124 @@ pub fn detectRuntimeVersion() WindowsVersion {
return @intToEnum(WindowsVersion, version);
}
fn detectCpuModelArm64() !*const Target.Cpu.Model {
// Pull the CPU identifier from the registry.
// Assume max number of cores to be at 128.
const max_cpu_count = 128;
const cpu_count = getCpuCount();
if (cpu_count > max_cpu_count) return error.TooManyCpus;
const table_size = max_cpu_count * 3 + 1;
const actual_table_size = cpu_count * 3 + 1;
var table: [table_size]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
// Table sentinel
table[actual_table_size - 1] = .{
.QueryRoutine = null,
.Flags = 0,
.Name = null,
.EntryContext = null,
.DefaultType = 0,
.DefaultData = null,
.DefaultLength = 0,
};
// Technically, a registry value can be as long as 16k u16s. However, MS recommends storing
// values larger than 2048 in a file rather than directly in the registry, and since we
// are only accessing a system hive \Registry\Machine, we stick to MS guidelines.
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
const max_sz_value = 2048;
const key_name = std.unicode.utf8ToUtf16LeStringLiteral("Identifier");
var i: usize = 0;
var index: usize = 0;
while (i < cpu_count) : (i += 1) {
var buf: [max_sz_value]u16 = undefined;
var buf_uni = std.os.windows.UNICODE_STRING{
.Length = buf.len * 2,
.MaximumLength = buf.len * 2,
.Buffer = &buf,
};
var next_cpu_buf: [std.math.log2(max_cpu_count)]u8 = undefined;
const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{i});
var subkey: [std.math.log2(max_cpu_count) / 2]u16 = undefined;
const subkey_len = try std.unicode.utf8ToUtf16Le(&subkey, next_cpu);
subkey[subkey_len] = 0;
table[index] = .{
.QueryRoutine = null,
.Flags = std.os.windows.RTL_QUERY_REGISTRY_SUBKEY | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
.Name = subkey[0..subkey_len :0],
.EntryContext = null,
.DefaultType = std.os.windows.REG_NONE,
.DefaultData = null,
.DefaultLength = 0,
};
table[index + 1] = .{
.QueryRoutine = null,
.Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
.Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),
.EntryContext = &buf_uni,
.DefaultType = std.os.windows.REG_NONE,
.DefaultData = null,
.DefaultLength = 0,
};
table[index + 2] = .{
.QueryRoutine = null,
.Flags = std.os.windows.RTL_QUERY_REGISTRY_TOPKEY,
.Name = null,
.EntryContext = null,
.DefaultType = std.os.windows.REG_NONE,
.DefaultData = null,
.DefaultLength = 0,
};
index += 3;
}
const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
const res = std.os.windows.ntdll.RtlQueryRegistryValues(
std.os.windows.RTL_REGISTRY_ABSOLUTE,
topkey,
&table,
null,
null,
);
switch (res) {
.SUCCESS => {},
else => return error.QueryRegistryFailed,
}
// Parse the models from strings
i = 0;
index = 0;
while (i < cpu_count) : (i += 1) {
const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[index + 1].EntryContext);
index += 3;
var identifier_buf: [max_sz_value * 2]u8 = undefined;
const len = try std.unicode.utf16leToUtf8(&identifier_buf, entry.Buffer[0 .. entry.Length / 2]);
const identifier = identifier_buf[0..len];
_ = identifier;
}
return &Target.aarch64.cpu.microsoft_sq3;
}
fn detectNativeCpuAndFeaturesArm64() Target.Cpu {
const Feature = Target.aarch64.Feature;
const model = detectCpuModelArm64() catch Target.Cpu.Model.generic(.aarch64);
var cpu = Target.Cpu{
.arch = .aarch64,
.model = Target.Cpu.Model.generic(.aarch64),
.features = Target.Cpu.Feature.Set.empty,
.model = model,
.features = model.features,
};
if (IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE)) {