Skip to content

Commit 987482a

Browse files
committed
Address static analysis cleanup findings
1 parent 400e4bc commit 987482a

9 files changed

Lines changed: 116 additions & 32 deletions

File tree

‎.agents/sow/current/SOW-0015-20260605-codacy-scope-and-maintainability.md‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,45 @@ Open decisions:
424424
- `codacy-analysis analyze . --output-format json`: exit status 0, 0 issues, 1 known Revive adapter invocation error:
425425
- Revive error: `Failed to run revive: findings is not iterable`.
426426

427+
### 2026-06-06 - GitHub AI Findings Triage
428+
429+
- Opened `https://github.com/netdata/plugin-ipc/security/quality/ai-findings` with the authenticated Playwright browser.
430+
- GitHub displayed 15 AI findings across 5 files, not 5 individual findings:
431+
- `src/crates/netipc/src/service/raw/server_windows.rs`: 2 findings.
432+
- `src/go/pkg/netipc/protocol/lookup_common.go`: 5 findings.
433+
- `src/go/pkg/netipc/transport/posix/uds_stale.go`: 3 findings.
434+
- `src/go/pkg/netipc/transport/windows/pipe_session.go`: 2 findings.
435+
- `src/libnetdata/netipc/src/transport/posix/netipc_uds_send.c`: 3 findings.
436+
- Local verification found stale or false-positive protocol findings:
437+
- `src/go/pkg/netipc/protocol/lookup_common.go:19` already has the `LookupLabelView` doc comment.
438+
- `src/go/pkg/netipc/protocol/lookup_common.go:452` already uses `count := int(itemCount)` after the prior overflow check.
439+
- `src/go/pkg/netipc/protocol/frame.go:75` defines package-level `ne = binary.NativeEndian`.
440+
- `src/go/pkg/netipc/protocol/frame.go:100` defines package-level `Align8`.
441+
- Accepted real low-risk findings:
442+
- Simplify redundant Rust Windows SHM finalization match.
443+
- Document the Rust Windows SHM cleanup order.
444+
- Add a doc comment for unexported Go `maxIntValue` to satisfy the scanner without behavior change.
445+
- Cache effective UID before POSIX stale-unlink directory stat.
446+
- Use a bounded UDS stale-candidate dial timeout and retry transient `ECONNREFUSED` before unlinking.
447+
- Add idiomatic Windows Go `Role()` accessors while preserving deprecated `GetRole()` wrappers.
448+
- Replace the Windows named-pipe spin-loop magic number with a named constant.
449+
- Split C UDS send compound conditions and local min expressions without changing send behavior.
450+
- Same-pattern search found `src/go/pkg/netipc/transport/windows/shm.go:163` also exposed only `GetRole()` while POSIX SHM exposes `Role()`; added the same additive `Role()` API there and kept `GetRole()` for compatibility.
451+
- Validation for this AI-findings pass:
452+
- `gofmt` on touched Go files: passed.
453+
- `cargo fmt --manifest-path src/crates/netipc/Cargo.toml`: passed.
454+
- `git diff --check`: passed.
455+
- `cd src/go && go test ./pkg/netipc/protocol ./pkg/netipc/transport/posix`: passed.
456+
- `cd src/go && go test ./...`: passed.
457+
- `cd src/go && GOOS=windows GOARCH=amd64 go test -c -o /tmp/netipc-transport-windows.test.exe ./pkg/netipc/transport/windows`: passed.
458+
- `ssh win11 'cd /tmp/plugin-ipc-ai-findings/src/go && MSYSTEM=MSYS go test ./pkg/netipc/transport/windows'`: passed.
459+
- `cargo test --manifest-path src/crates/netipc/Cargo.toml`: passed, 332 Rust unit tests.
460+
- `cmake --build build`: passed.
461+
- `/usr/bin/ctest --test-dir build --output-on-failure`: passed, 46/46 tests.
462+
- `bash .agents/sow/audit.sh`: passed.
463+
- `codacy-analysis analyze --files ... --output-format json`: exit status 0, 0 issues, 1 known Revive adapter error:
464+
- Revive error: `Failed to run revive: findings is not iterable`.
465+
427466
## Validation
428467

429468
Acceptance criteria evidence:

‎src/crates/netipc/src/service/raw/server_windows.rs‎

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,14 @@ impl ManagedServer {
5454
continue;
5555
}
5656

57-
let shm = match self.finalize_windows_shm(&session, prepared_shm) {
58-
Some(shm) => Some(shm),
59-
None if session.selected_profile == WIN_SHM_PROFILE_HYBRID
60-
|| session.selected_profile == WIN_SHM_PROFILE_BUSYWAIT =>
61-
{
62-
drop(session);
63-
continue;
64-
}
65-
None => None,
66-
};
57+
let shm = self.finalize_windows_shm(&session, prepared_shm);
58+
if shm.is_none()
59+
&& (session.selected_profile == WIN_SHM_PROFILE_HYBRID
60+
|| session.selected_profile == WIN_SHM_PROFILE_BUSYWAIT)
61+
{
62+
drop(session);
63+
continue;
64+
}
6765

6866
let expected_method_code = self.expected_method_code;
6967
let handler = self.handler.clone();
@@ -154,6 +152,7 @@ impl ManagedServer {
154152
return None;
155153
}
156154
let mut prepared = prepared?;
155+
// Keep the negotiated context and destroy every unused prepared context.
157156
let selected = prepared.take(profile);
158157
prepared.destroy_all();
159158
selected

‎src/go/pkg/netipc/protocol/lookup_common.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func invalidSourceString(data []byte, requireNonEmpty bool) bool {
2626
return (requireNonEmpty && len(data) == 0) || bytes.IndexByte(data, 0) >= 0
2727
}
2828

29+
// maxIntValue returns the maximum value representable by int on this platform.
2930
func maxIntValue() int {
3031
return int(^uint(0) >> 1)
3132
}

‎src/go/pkg/netipc/transport/posix/uds_stale.go‎

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"os"
99
"syscall"
10+
"time"
1011
)
1112

1213
type staleResult int
@@ -17,27 +18,52 @@ const (
1718
staleLiveServer staleResult = 2
1819
)
1920

21+
const (
22+
staleDialAttempts = 3
23+
staleDialRetryDelay = 50 * time.Millisecond
24+
staleDialTimeout = 1 * time.Second
25+
)
26+
2027
func runDirAllowsStaleUnlink(runDir string) bool {
28+
euid := uint32(os.Geteuid())
2129
info, err := os.Stat(runDir)
2230
if err != nil || !info.IsDir() {
2331
return false
2432
}
2533
st, ok := info.Sys().(*syscall.Stat_t)
26-
if !ok || st.Uid != uint32(os.Geteuid()) {
34+
if !ok || st.Uid != euid {
2735
return false
2836
}
2937
return info.Mode().Perm()&0022 == 0
3038
}
3139

40+
func dialStaleCandidate(path string) error {
41+
var err error
42+
for attempt := 0; attempt < staleDialAttempts; attempt++ {
43+
var conn net.Conn
44+
conn, err = net.DialTimeout("unixpacket", path, staleDialTimeout)
45+
if err == nil {
46+
_ = conn.Close()
47+
return nil
48+
}
49+
if !errors.Is(err, syscall.ECONNREFUSED) {
50+
return err
51+
}
52+
if attempt+1 < staleDialAttempts {
53+
time.Sleep(staleDialRetryDelay)
54+
}
55+
}
56+
return err
57+
}
58+
3259
func checkAndRecoverStale(path string, allowStaleUnlink bool) staleResult {
3360
_, err := os.Stat(path)
3461
if err != nil {
3562
return staleNotExist
3663
}
3764

38-
conn, err := net.Dial("unixpacket", path)
65+
err = dialStaleCandidate(path)
3966
if err == nil {
40-
_ = conn.Close()
4167
return staleLiveServer
4268
}
4369

‎src/go/pkg/netipc/transport/windows/pipe_integration_test.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,8 +1073,8 @@ func TestPipeHandleAndRole(t *testing.T) {
10731073
if client.Handle() == syscall.InvalidHandle || server.Handle() == syscall.InvalidHandle {
10741074
t.Fatal("session handles should be valid")
10751075
}
1076-
if client.GetRole() != RoleClient || server.GetRole() != RoleServer {
1077-
t.Fatalf("unexpected roles client=%d server=%d", client.GetRole(), server.GetRole())
1076+
if client.Role() != RoleClient || server.Role() != RoleServer {
1077+
t.Fatalf("unexpected roles client=%d server=%d", client.Role(), server.Role())
10781078
}
10791079
}
10801080

‎src/go/pkg/netipc/transport/windows/pipe_session.go‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ const (
1515
RoleServer Role = 2
1616
)
1717

18+
// spinWaitIterations limits cooperative polling before falling back to sleep.
19+
const spinWaitIterations = 256
20+
1821
// ClientConfig configures a client connection.
1922
type ClientConfig struct {
2023
SupportedProfiles uint32
@@ -72,10 +75,16 @@ func (s *Session) Handle() syscall.Handle {
7275
}
7376

7477
// Role returns the session role.
75-
func (s *Session) GetRole() Role {
78+
func (s *Session) Role() Role {
7679
return s.role
7780
}
7881

82+
// GetRole returns the session role.
83+
// Deprecated: use Role.
84+
func (s *Session) GetRole() Role {
85+
return s.Role()
86+
}
87+
7988
// WaitReadable waits until bytes are available to read or the timeout expires.
8089
func (s *Session) WaitReadable(timeoutMs uint32) (bool, error) {
8190
if s.handle == syscall.InvalidHandle {
@@ -101,7 +110,7 @@ func (s *Session) WaitReadable(timeoutMs uint32) (bool, error) {
101110
}
102111
if !yielded {
103112
yielded = true
104-
for i := 0; i < 256; i++ {
113+
for i := 0; i < spinWaitIterations; i++ {
105114
procSwitchToThread.Call()
106115
available, err = peekNamedPipeAvailable(s.handle)
107116
if err != nil {

‎src/go/pkg/netipc/transport/windows/shm.go‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,11 @@ type WinShmContext struct {
160160
}
161161

162162
// Role returns the context role.
163-
func (c *WinShmContext) GetRole() WinShmRole { return c.role }
163+
func (c *WinShmContext) Role() WinShmRole { return c.role }
164+
165+
// GetRole returns the context role.
166+
// Deprecated: use Role.
167+
func (c *WinShmContext) GetRole() WinShmRole { return c.Role() }
164168

165169
// ---------------------------------------------------------------------------
166170
// Server API

‎src/go/pkg/netipc/transport/windows/shm_test.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ func TestWinShmCreateAttachAndCloseValidation(t *testing.T) {
111111
}
112112
defer client.WinShmClose()
113113

114-
if server.GetRole() != WinShmRoleServer {
115-
t.Fatalf("server role = %d, want %d", server.GetRole(), WinShmRoleServer)
114+
if server.Role() != WinShmRoleServer {
115+
t.Fatalf("server role = %d, want %d", server.Role(), WinShmRoleServer)
116116
}
117-
if client.GetRole() != WinShmRoleClient {
118-
t.Fatalf("client role = %d, want %d", client.GetRole(), WinShmRoleClient)
117+
if client.Role() != WinShmRoleClient {
118+
t.Fatalf("client role = %d, want %d", client.Role(), WinShmRoleClient)
119119
}
120120
}
121121

‎src/libnetdata/netipc/src/transport/posix/netipc_uds_send.c‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
#include <string.h>
44

5+
static size_t min_size(size_t a, size_t b)
6+
{
7+
return a < b ? a : b;
8+
}
9+
510
static bool tracks_client_request(const nipc_uds_session_t *session,
611
const nipc_header_t *hdr)
712
{
@@ -14,7 +19,10 @@ static void update_inflight_after_send(nipc_uds_session_t *session,
1419
bool tracked,
1520
nipc_uds_error_t err)
1621
{
17-
if (!tracked || err == NIPC_UDS_OK)
22+
if (!tracked)
23+
return;
24+
25+
if (err == NIPC_UDS_OK)
1826
return;
1927

2028
if (err == NIPC_UDS_ERR_SEND)
@@ -66,9 +74,11 @@ static nipc_uds_error_t validate_outbound_limits(nipc_uds_session_t *session,
6674
uint32_t max_batch;
6775
outbound_limits(session, hdr, &max_payload, &max_batch);
6876

69-
if (payload_len <= UINT32_MAX &&
70-
(max_payload == 0 || payload_len <= max_payload) &&
71-
(max_batch == 0 || hdr->item_count <= max_batch))
77+
bool payload_fits_u32 = payload_len <= UINT32_MAX;
78+
bool payload_within_limit = max_payload == 0 || payload_len <= max_payload;
79+
bool batch_within_limit = max_batch == 0 || hdr->item_count <= max_batch;
80+
81+
if (payload_fits_u32 && payload_within_limit && batch_within_limit)
7282
return NIPC_UDS_OK;
7383

7484
if (tracked)
@@ -154,9 +164,7 @@ static nipc_uds_error_t send_chunked(nipc_uds_session_t *session,
154164
return NIPC_UDS_ERR_BAD_PARAM;
155165

156166
size_t remaining = payload_len;
157-
size_t first_chunk_payload = remaining < chunk_payload_budget
158-
? remaining
159-
: chunk_payload_budget;
167+
size_t first_chunk_payload = min_size(remaining, chunk_payload_budget);
160168
remaining -= first_chunk_payload;
161169

162170
uint32_t continuation_chunks = 0;
@@ -175,9 +183,7 @@ static nipc_uds_error_t send_chunked(nipc_uds_session_t *session,
175183
remaining = payload_len - first_chunk_payload;
176184

177185
for (uint32_t ci = 1; ci < chunk_count; ci++) {
178-
size_t this_chunk = remaining < chunk_payload_budget
179-
? remaining
180-
: chunk_payload_budget;
186+
size_t this_chunk = min_size(remaining, chunk_payload_budget);
181187

182188
err = send_continuation_chunk(session, hdr, src, this_chunk, ci,
183189
chunk_count, (uint32_t)total_msg,

0 commit comments

Comments
 (0)