Skip to content

Hongshi.guo/wina 1171 cpu info#1

Open
leika wants to merge 2 commits intomainfrom
hongshi.guo/WINA-1171_cpu_info
Open

Hongshi.guo/wina 1171 cpu info#1
leika wants to merge 2 commits intomainfrom
hongshi.guo/WINA-1171_cpu_info

Conversation

@leika
Copy link
Copy Markdown
Member

@leika leika commented May 30, 2025

What does this PR do?

Motivation

Describe how you validated your changes

Possible Drawbacks / Trade-offs

Additional Notes

Summary by CodeRabbit

  • Bug Fixes
    • Improved accuracy of CPU core and processor information reported on Windows systems.
  • Chores
    • Enhanced internal handling of CPU data collection on Windows for better reliability.
  • Documentation
    • Added a release note describing the fix for CPU core reporting on Windows.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented May 30, 2025

Walkthrough

The update refactors CPU information retrieval on Windows by removing platform-specific Go implementations and introducing a unified C-based helper (cpu_windows.c and header) called from Go. The Go code now gathers CPU topology and cache data via this C function, supplements it with registry and system API calls, and improves error handling. Test logging and release notes are also updated.

Changes

File(s) Change Summary
pkg/gohai/cpu/cpu_windows_386.go, pkg/gohai/cpu/cpu_windows_amd64.go Deleted platform-specific Go implementations for Windows CPU info retrieval.
pkg/gohai/cpu/cpu_windows.c, pkg/gohai/cpu/cpu_windows.h Added C source and header for CPU info retrieval using Windows API, defining and populating a CPU_INFO struct.
pkg/gohai/cpu/cpu_windows.go Refactored Go logic to use the C helper for CPU info, restructured data extraction, and improved error handling.
pkg/gohai/cpu/cpu_test.go Added test build constraint, imported logging, and inserted log statements in the test function.
releasenotes/notes/windows-agent-cpu-info-18e54faf1ffbd15a.yaml Added release note documenting the Windows agent CPU info fix.

Sequence Diagram(s)

sequenceDiagram
    participant GoCode as Go CPU Info Collector
    participant CHelper as C Helper (cpu_windows.c)
    participant WinAPI as Windows API
    participant Registry as Windows Registry

    GoCode->>CHelper: computeCoresAndProcessors(&CPU_INFO)
    CHelper->>WinAPI: GetLogicalProcessorInformationEx
    CHelper-->>GoCode: CPU_INFO struct (cores, caches, etc.)
    GoCode->>Registry: Query CPU MHz, Model, Vendor, Family
    Registry-->>GoCode: Registry values
    GoCode->>WinAPI: GetSystemInfo
    WinAPI-->>GoCode: System info (model, stepping)
    GoCode-->>GoCode: Assemble Info struct
Loading

Poem

🐇
In Windows warren, deep and wide,
The C code hops to fetch inside—
Cores and caches, NUMA too,
With Go and C now joined anew!
Logs will tell what tests reveal,
A CPU tale, robust and real.
—Rabbit, with a registry squeal!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: build linters: plugin(pkgconfigusage): plugin "pkgconfigusage" not found
Failed executing command with error: build linters: plugin(pkgconfigusage): plugin "pkgconfigusage" not found

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
pkg/gohai/cpu/cpu_windows.h (1)

8-20: Consider adding documentation and checking data types.

The CPU_INFO struct definition looks correct, but consider these improvements:

  1. Add brief documentation comments for each field to clarify their purpose
  2. Verify that unsigned long long is appropriate for cache sizes - while it can handle large values, consider if DWORD64 or SIZE_T would be more appropriate for Windows API consistency
// CPU_INFO mirrors the Go struct
typedef struct {
+    int corecount;              // Number of physical CPU cores
+    int logicalcount;           // Number of logical processors (including hyperthreading)
+    int pkgcount;               // Number of physical CPU packages/sockets
+    int numaNodeCount;          // Number of NUMA nodes
+    int relationGroups;         // Number of processor groups
+    int maxProcsInGroups;       // Maximum processors across all groups
+    int activeProcsInGroups;    // Active processors across all groups
+    unsigned long long l1CacheSize;  // Total L1 cache size in bytes
+    unsigned long long l2CacheSize;  // Total L2 cache size in bytes
+    unsigned long long l3CacheSize;  // Total L3 cache size in bytes
} CPU_INFO;
pkg/gohai/cpu/cpu_test.go (2)

13-13: Consider using testing.T.Log instead of log.Infof.

Using log.Infof in tests may not be the best approach as it depends on the global logger configuration and may not appear in test output.

Consider using t.Logf for test-specific logging:

-	"github.com/DataDog/datadog-agent/pkg/util/log"

And replace the log statements with t.Logf calls.


69-84: Improve test logging approach.

While the logging provides useful debugging information, consider these improvements:

  1. Use t.Logf instead of log.Infof for better integration with test output
  2. Consider consolidating the logging or making it conditional (e.g., only when a verbose flag is set)
-	// output CPUCores, CPULogicalProcessors, Family, Mhz, Model, ModelName, Stepping, VendorID, CacheSizeKB, CacheSizeL1Bytes, CacheSizeL2Bytes, CacheSizeL3Bytes, CPUNumaNodes, CPUPkgs
-	log.Infof("CPUCores: %s", decodedCPU.CPUCores)
-	log.Infof("CPULogicalProcessors: %s", decodedCPU.CPULogicalProcessors)
-	log.Infof("Family: %s", decodedCPU.Family)
-	log.Infof("Mhz: %s", decodedCPU.Mhz)
-	log.Infof("Model: %s", decodedCPU.Model)
-	log.Infof("ModelName: %s", decodedCPU.ModelName)
-	log.Infof("Stepping: %s", decodedCPU.Stepping)
-	log.Infof("VendorID: %s", decodedCPU.VendorID)
-	log.Infof("CacheSizeKB: %s", decodedCPU.CacheSizeKB)
-	log.Infof("CacheSizeL1Bytes: %s", decodedCPU.CacheSizeL1Bytes)
-	log.Infof("CacheSizeL2Bytes: %s", decodedCPU.CacheSizeL2Bytes)
-	log.Infof("CacheSizeL3Bytes: %s", decodedCPU.CacheSizeL3Bytes)
-	log.Infof("CPUNumaNodes: %s", decodedCPU.CPUNumaNodes)
-	log.Infof("CPUPkgs: %s", decodedCPU.CPUPkgs)
+	// Log CPU info for debugging
+	t.Logf("CPU Info - Cores: %s, LogicalProcs: %s, Family: %s, MHz: %s, Model: %s, ModelName: %s, Stepping: %s, VendorID: %s",
+		decodedCPU.CPUCores, decodedCPU.CPULogicalProcessors, decodedCPU.Family, decodedCPU.Mhz, 
+		decodedCPU.Model, decodedCPU.ModelName, decodedCPU.Stepping, decodedCPU.VendorID)
+	t.Logf("Cache Info - KB: %s, L1: %s, L2: %s, L3: %s, NUMA: %s, Pkgs: %s",
+		decodedCPU.CacheSizeKB, decodedCPU.CacheSizeL1Bytes, decodedCPU.CacheSizeL2Bytes, 
+		decodedCPU.CacheSizeL3Bytes, decodedCPU.CPUNumaNodes, decodedCPU.CPUPkgs)
pkg/gohai/cpu/cpu_windows.go (2)

49-67: Consider consistent field naming in the cpuInfo struct.

The struct has a mix of exported (uppercase) and unexported (lowercase) fields. Since all fields appear to be used internally within the package, consider making the naming convention consistent by either exporting all fields or keeping them all unexported.


69-76: Improve the regex pattern for more robust extraction.

The current regex pattern has a few limitations:

  • Uses [0-9]* which matches zero or more digits (should be [0-9]+ for one or more)
  • Requires a trailing space which might not always be present
  • Could match anywhere in the string without anchoring

Consider this improved implementation:

 func extract(caption, field string) string {
-	re := regexp.MustCompile(fmt.Sprintf("%s [0-9]* ", field))
+	re := regexp.MustCompile(fmt.Sprintf(`\b%s\s+([0-9]+)\b`, field))
 	matches := re.FindStringSubmatch(caption)
-	if len(matches) > 0 {
-		return strings.Split(matches[0], " ")[1]
+	if len(matches) > 1 {
+		return matches[1]
 	}
 	return ""
 }

This uses word boundaries (\b), captures the numeric value directly, and ensures at least one digit is present.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69e3afe and 1b4d4c0.

📒 Files selected for processing (7)
  • pkg/gohai/cpu/cpu_test.go (2 hunks)
  • pkg/gohai/cpu/cpu_windows.c (1 hunks)
  • pkg/gohai/cpu/cpu_windows.go (2 hunks)
  • pkg/gohai/cpu/cpu_windows.h (1 hunks)
  • pkg/gohai/cpu/cpu_windows_386.go (0 hunks)
  • pkg/gohai/cpu/cpu_windows_amd64.go (0 hunks)
  • releasenotes/notes/windows-agent-cpu-info-18e54faf1ffbd15a.yaml (1 hunks)
💤 Files with no reviewable changes (2)
  • pkg/gohai/cpu/cpu_windows_386.go
  • pkg/gohai/cpu/cpu_windows_amd64.go
🧰 Additional context used
🧬 Code Graph Analysis (2)
pkg/gohai/cpu/cpu_test.go (1)
pkg/util/log/log.go (1)
  • Infof (844-846)
pkg/gohai/cpu/cpu_windows.c (1)
pkg/gohai/cpu/cpu_windows.h (1)
  • computeCoresAndProcessors (23-23)
🔇 Additional comments (9)
pkg/gohai/cpu/cpu_windows.h (1)

22-23: Function signature looks correct.

The function declaration is appropriate for its intended use - taking a pointer to the struct to populate it and returning an int for error codes.

releasenotes/notes/windows-agent-cpu-info-18e54faf1ffbd15a.yaml (1)

9-12: Release note accurately describes the fix.

The release note correctly identifies the issue being fixed (inaccurate cpu_cores reporting by gohai) and references the appropriate JIRA ticket. The format follows the expected reStructuredText structure.

pkg/gohai/cpu/cpu_windows.c (7)

5-8: Include statements look appropriate.

The necessary headers are included for Windows API functions and standard C library functions.


17-18: Good practice: Zero-initialize the struct.

Using memset to zero-initialize the struct ensures clean data accumulation and prevents undefined behavior from uninitialized fields.


20-24: Correct API usage for buffer size determination.

The two-step approach (first call to get buffer size, then allocate and call again) follows the standard Windows API pattern for variable-length data structures.


61-73: Cache size accumulation logic is correct.

The switch statement correctly handles different cache levels and accumulates their sizes. This approach properly sums cache sizes across all processor cores.


31-34: Good error handling and cleanup.

The function properly cleans up allocated memory on API failure and returns the appropriate error code.


91-92: Proper cleanup and success return.

Memory is freed and the function returns 0 to indicate success, following good C programming practices.


42-55:

Details

✅ Verification successful

Verify logical processor counting logic.

The bit manipulation logic for counting logical processors appears correct, but there's a potential issue with the GROUP_AFFINITY structure access pattern.

Let me verify the Windows API documentation for the correct way to iterate through processor groups and count logical processors:


🌐 Web query:

Windows API GetLogicalProcessorInformationEx SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX RelationProcessorCore structure layout and GROUP_AFFINITY usage

💡 Result:

The GetLogicalProcessorInformationEx function is a Windows API that retrieves detailed information about logical processors and their relationships in a system. When used with RelationProcessorCore, it provides specific information about processor cores, including their efficiency classes on hybrid CPU architectures.

Function Overview

GetLogicalProcessorInformationEx has the following syntax:

BOOL GetLogicalProcessorInformationEx(
  [in] LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType,
  [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
  [in, out] PDWORD ReturnedLength
);

The function returns TRUE if successful and writes at least one SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX structure to the output buffer[1]. This function is particularly useful for systems with more than 64 logical processors, as it's group-aware, unlike its predecessor GetLogicalProcessorInformation[2].

SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Structure

When RelationProcessorCore is specified as the RelationshipType parameter, the function returns information about processor cores. The structure is defined as:

typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
  LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
  DWORD Size;
  union {
    PROCESSOR_RELATIONSHIP Processor;
    NUMA_NODE_RELATIONSHIP NumaNode;
    CACHE_RELATIONSHIP Cache;
    GROUP_RELATIONSHIP Group;
  } DUMMYUNIONNAME;
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;

For RelationProcessorCore, the Processor field in the union contains valid data[10]. This PROCESSOR_RELATIONSHIP structure includes important information such as:

  1. The efficiency class of the processor core (on hybrid CPUs)
  2. Group affinity information

PROCESSOR_RELATIONSHIP and Efficiency Class

On hybrid CPU architectures (like Intel's performance hybrid architecture), the PROCESSOR_RELATIONSHIP structure includes an EfficiencyClass field that indicates the power-to-performance ratio of a logical processor[6].

  • Higher EfficiencyClass values indicate higher performance but lower power efficiency
  • Lower EfficiencyClass values indicate lower performance but higher power efficiency

This value is particularly useful for task scheduling based on performance or power requirements. For example, you might assign background tasks to cores with a lower efficiency class value and foreground tasks to cores with a higher efficiency class value[6].

GROUP_AFFINITY Usage

The GROUP_AFFINITY structure is a critical component when working with the GetLogicalProcessorInformationEx function, especially for systems with more than 64 logical processors. It contains:

typedef struct _GROUP_AFFINITY {
  KAFFINITY Mask;
  WORD Group;
  WORD Reserved[3];
} GROUP_AFFINITY, *PGROUP_AFFINITY;

The structure has the following important members:

  1. Mask: A KAFFINITY value that serves as a 64-bit bitmask identifying processors within a group[7].
  2. Group: Specifies the processor group number[7].
  3. Reserved: Three WORD values reserved for future use (must be set to zero)[7].

When the RelationProcessorCore is specified, the PROCESSOR_RELATIONSHIP structure contains a groupMask array of GROUP_AFFINITY structures[6][8]. For processor cores, the groupCount is typically 1, meaning there's usually just one GROUP_AFFINITY structure per processor core[8].

Example Usage

In practical usage, you would typically:

  1. First call the function with a NULL buffer to get the required size
  2. Allocate memory for the buffer
  3. Call the function again to retrieve the information
  4. Process the returned structures based on their Relationship type

For example, when processing RelationProcessorCore relationships:

// For cores
if (info.relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore) {
    PROCESSOR_RELATIONSHIP core = ((PROCESSOR_RELATIONSHIP) info);
    // For Core, groupCount is always 1
    GROUP_AFFINITY groupAffinity = core.groupMask[0];
    
    // On Windows 10 or later, you can access efficiency class
    int efficiencyClass = core.EfficiencyClass;
}

This pattern is demonstrated in various implementations, such as in the OSHI library[8] and the sysinfo Rust crate[9], which use this function to gather processor information.

Citations:


No issues found in logical processor counting logic

The implementation correctly iterates over Processor.GroupCount and uses each GROUP_AFFINITY.Mask to count set bits, matching the Windows API’s SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX semantics. Handling multiple groups ensures support for systems with over 64 logical processors, while on most machines GroupCount will be 1.

// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2014-present Datadog, Inc.

//go:build test
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reconsider the build constraint.

The //go:build test constraint restricts this test file to only compile when the test build tag is specified. This is unusual for test files, which typically compile during normal go test execution without special build tags.

Unless there's a specific reason for this constraint, remove it to allow normal test execution:

-//go:build test
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//go:build test
🤖 Prompt for AI Agents
In pkg/gohai/cpu/cpu_test.go at line 5, the build constraint `//go:build test`
limits the test file to compile only with the `test` build tag, which is not
standard for Go test files. Remove this build constraint line entirely to allow
the test file to compile and run normally with the standard `go test` command.

Comment on lines +26 to +29
buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(buflen);
if (buffer == NULL) {
return ERROR_OUTOFMEMORY;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing memory allocation header.

The code uses malloc but doesn't include <stdlib.h>. This could cause compilation warnings or errors.

 #include "cpu_windows.h"
 #include <windows.h>
 #include <stdio.h>
+#include <stdlib.h>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(buflen);
if (buffer == NULL) {
return ERROR_OUTOFMEMORY;
}
#include "cpu_windows.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
🤖 Prompt for AI Agents
In pkg/gohai/cpu/cpu_windows.c around lines 26 to 29, the code uses malloc for
memory allocation but does not include the <stdlib.h> header, which is required
for malloc declaration. Add #include <stdlib.h> at the top of the file to ensure
proper declaration and avoid compilation warnings or errors.

Comment on lines +79 to +85
case RelationGroup:
cpuInfo->relationGroups = ptr->Group.MaximumGroupCount;
for (WORD i = 0; i < ptr->Group.ActiveGroupCount; i++) {
cpuInfo->maxProcsInGroups += ptr->Group.GroupInfo[i].MaximumProcessorCount;
cpuInfo->activeProcsInGroups += ptr->Group.GroupInfo[i].ActiveProcessorCount;
}
break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential issue with group information handling.

The logic assumes there's only one RelationGroup entry, but the loop overwrites relationGroups and continues accumulating maxProcsInGroups and activeProcsInGroups. This could lead to incorrect values if multiple group entries exist.

             case RelationGroup:
-                cpuInfo->relationGroups = ptr->Group.MaximumGroupCount;
+                cpuInfo->relationGroups += ptr->Group.MaximumGroupCount;
                 for (WORD i = 0; i < ptr->Group.ActiveGroupCount; i++) {
                     cpuInfo->maxProcsInGroups += ptr->Group.GroupInfo[i].MaximumProcessorCount;
                     cpuInfo->activeProcsInGroups += ptr->Group.GroupInfo[i].ActiveProcessorCount;
                 }
                 break;

Or consider whether relationGroups should store the total count or just the maximum group count from the last entry, based on the intended semantics.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case RelationGroup:
cpuInfo->relationGroups = ptr->Group.MaximumGroupCount;
for (WORD i = 0; i < ptr->Group.ActiveGroupCount; i++) {
cpuInfo->maxProcsInGroups += ptr->Group.GroupInfo[i].MaximumProcessorCount;
cpuInfo->activeProcsInGroups += ptr->Group.GroupInfo[i].ActiveProcessorCount;
}
break;
case RelationGroup:
cpuInfo->relationGroups += ptr->Group.MaximumGroupCount;
for (WORD i = 0; i < ptr->Group.ActiveGroupCount; i++) {
cpuInfo->maxProcsInGroups += ptr->Group.GroupInfo[i].MaximumProcessorCount;
cpuInfo->activeProcsInGroups += ptr->Group.GroupInfo[i].ActiveProcessorCount;
}
break;
🤖 Prompt for AI Agents
In pkg/gohai/cpu/cpu_windows.c around lines 79 to 85, the code assumes a single
RelationGroup entry but may process multiple, causing relationGroups to be
overwritten and maxProcsInGroups and activeProcsInGroups to accumulate
incorrectly. Modify the code to either accumulate relationGroups across all
RelationGroup entries or clarify if it should only store the maximum group count
from the last entry, ensuring consistent and correct aggregation of group
information.

Comment on lines +100 to 107
// Get additional CPU information from Windows registry
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
registryHive,
registry.QUERY_VALUE)
if err == nil {
defer k.Close()

dw, _, err := k.GetIntegerValue("~MHz")
cpuInfo.Mhz = utils.NewValueFrom(float64(dw), err)

s, _, err := k.GetStringValue("ProcessorNameString")
cpuInfo.ModelName = utils.NewValueFrom(s, err)

s, _, err = k.GetStringValue("VendorIdentifier")
cpuInfo.VendorID = utils.NewValueFrom(s, err)

s, _, err = k.GetStringValue("Identifier")
if err == nil {
cpuInfo.Family = utils.NewValue(extract(s, "Family"))
} else {
cpuInfo.Family = utils.NewErrorValue[string](err)
}
} else {
cpuInfo.Mhz = utils.NewErrorValue[float64](err)
cpuInfo.ModelName = utils.NewErrorValue[string](err)
cpuInfo.VendorID = utils.NewErrorValue[string](err)
cpuInfo.Family = utils.NewErrorValue[string](err)
if err != nil {
log.Errorf("failed to open registry key: %v", err)
return &Info{}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Preserve CPU data from C function when registry access fails.

The current implementation returns an empty Info{} struct when the registry key cannot be opened, discarding potentially valid CPU topology data already retrieved from the C function (cores, logical processors, cache sizes, etc.).

Consider continuing with the data collection even if the registry access fails:

 	// Get additional CPU information from Windows registry
 	k, err := registry.OpenKey(registry.LOCAL_MACHINE,
 		registryHive,
 		registry.QUERY_VALUE)
 	if err != nil {
 		log.Errorf("failed to open registry key: %v", err)
-		return &Info{}
+		// Continue with partial data from C function
+	} else {
+		defer k.Close()
+		
+		if mhz, _, err := k.GetIntegerValue("~MHz"); err == nil {
+			cInfo.Mhz = float64(mhz)
+		}
+		// ... rest of registry reads ...
 	}
-	defer k.Close()

This ensures that CPU topology information is still available even if registry-based details (MHz, vendor ID, etc.) cannot be retrieved.

🤖 Prompt for AI Agents
In pkg/gohai/cpu/cpu_windows.go around lines 100 to 107, the code returns an
empty Info struct when failing to open the registry key, discarding CPU data
obtained from the C function. Modify the error handling to log the error but
continue execution, returning the partially populated Info struct with the data
collected so far instead of an empty one. This preserves CPU topology
information even if registry access fails.

Comment on lines +21 to +26
GetLogicalProcessorInformationEx(RelationAll, NULL, &buflen);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}

buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(buflen);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Corgea found a CWE-190: Integer Overflow or Wraparound vulnerability that is rated as 🔴 High.
View in Corgea.

🎟️Issue Explanation: The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control.

More issue detailsThe code risks integer overflow by assuming a calculated buffer size will stay positive, which could lead to resource mismanagement or improper execution control.

-"buflen" could exceed its maximum value during calculation, converting it into a negative.
-Overflow can trick "malloc(buflen)" into allocating insufficient memory.
-If exploited, this can disrupt tasks or expose sensitive data like processor info.
🪄Fix Suggestion:
Fix DetailsThe fix prevents integer overflow and zero-size allocations by checking if "buflen" is within valid limits before allocating memory. This ensures safe resource management and guards against potential overflow and wraparound vulnerabilities.
- An overflow check is added: "if (buflen == 0 || buflen > SIZE_MAX)" to ensure safe memory allocation.
- Returns "ERROR_INVALID_PARAMETER" if conditions are met, preventing further execution with invalid buffer sizes.
- Typecasting "buflen" to "(size_t)" during allocation ensures correct memory size handling.

Suggested change
GetLogicalProcessorInformationEx(RelationAll, NULL, &buflen);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}
buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(buflen);
GetLogicalProcessorInformationEx(RelationAll, NULL, &buflen);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}
// Prevent integer overflow and zero-size allocation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant