-
Notifications
You must be signed in to change notification settings - Fork 75
Use non-blocking send on pid unset, retry on close #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
0d2e031
9bcd269
2b7c7b4
1bf8771
cd4b38d
f2c42d4
8e5d074
185bc5e
e1259eb
7867c2f
fdc60e4
d84de7c
afe7f92
9ba5dbb
c5852ce
2d13888
377e832
bdca1ac
a15b923
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,7 +49,7 @@ | |
| // Netlink groups. | ||
| const ( | ||
| NetlinkGroupNone = iota // Group 0 not used | ||
| NetlinkGroupReadLog // "best effort" read only socket | ||
| NetlinkGroupReadLog // "best effort" read only socket, defined in the kernel as AUDIT_NLGRP_READLOG | ||
| ) | ||
|
|
||
| // WaitMode is a flag to control the behavior of methods that abstract | ||
|
|
@@ -427,16 +427,11 @@ | |
| // become no-ops. | ||
| func (c *AuditClient) Close() error { | ||
| var err error | ||
|
|
||
| // Only unregister and close the socket once. | ||
| c.closeOnce.Do(func() { | ||
| if c.clearPIDOnClose { | ||
| // Unregister from the kernel for a clean exit. | ||
| status := AuditStatus{ | ||
| Mask: AuditStatusPID, | ||
| PID: 0, | ||
| } | ||
| err = c.set(status, NoWait) | ||
| err = c.closeAndUnsetPid() | ||
| } | ||
|
|
||
| err = errors.Join(err, c.Netlink.Close()) | ||
|
|
@@ -505,6 +500,63 @@ | |
| return &msg, nil | ||
| } | ||
|
|
||
| // unset our pid from the audit subsystem and close the socket. | ||
| // This is a sort of isolated refactor, meant to deal with the deadlocks that can happen when we're not careful with blocking operations throughout a lot of this code. | ||
| func (c *AuditClient) closeAndUnsetPid() error { | ||
| msg := syscall.NetlinkMessage{ | ||
| Header: syscall.NlMsghdr{ | ||
| Type: AuditSet, | ||
| Flags: syscall.NLM_F_REQUEST, | ||
| }, | ||
| Data: AuditStatus{ | ||
| Mask: AuditStatusPID, | ||
| PID: 0, | ||
| }.toWireFormat(), | ||
| } | ||
|
|
||
| noParse := func(bytes []byte) ([]syscall.NetlinkMessage, error) { | ||
| return nil, nil | ||
| } | ||
|
|
||
| // If our request to unset the PID would block, then try to drain events from | ||
| // the netlink socket, resend, try again. | ||
| // In netlink, EAGAIN usually indicates our read buffer is full. | ||
| // The auditd code (which I'm using as a reference implementation) doesn't wait for a response when unsetting the audit pid. | ||
| maxLoop := 5 | ||
| for i := 0; i < maxLoop; i++ { | ||
fearful-symmetry marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| _, err := c.Netlink.SendNoWait(msg) | ||
| // if we get an interrupt, retry the send | ||
| if err == nil { | ||
| return nil | ||
| } else if errors.Is(err, syscall.EINTR) { | ||
|
||
| // got interrupt, try again | ||
| continue | ||
| } else if errors.Is(err, syscall.EAGAIN) { | ||
| maxRecv := 10000 | ||
| // send would block, try to drain the receive socket | ||
| for i := 0; i < maxRecv; i++ { | ||
fearful-symmetry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| _, err = c.Netlink.Receive(true, noParse) | ||
| if errors.Is(err, syscall.EAGAIN) { | ||
| // receive would block, try to send again | ||
| break | ||
| } else if err == nil || errors.Is(err, syscall.EINTR) || errors.Is(err, syscall.ENOBUFS) { | ||
| // retry the receive | ||
| continue | ||
| } else { | ||
| // if we have another kind of error, just bail and return that error. | ||
| return err | ||
| } | ||
fearful-symmetry marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
fearful-symmetry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| // if we get another error from the send, return that up | ||
| return err | ||
| } | ||
fearful-symmetry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| } | ||
| // we may not want to treat this as a hard error? | ||
| return fmt.Errorf("could not unset pid from audit after retries") | ||
| } | ||
|
|
||
| func (c *AuditClient) set(status AuditStatus, mode WaitMode) error { | ||
| msg := syscall.NetlinkMessage{ | ||
| Header: syscall.NlMsghdr{ | ||
|
|
@@ -560,7 +612,7 @@ | |
| // https://github.com/linux-audit/audit-kernel/blob/v4.7/include/uapi/linux/audit.h#L318-L325 | ||
| type AuditStatusMask uint32 | ||
|
|
||
| // Mask types for AuditStatus. | ||
| // Mask types for AuditStatus. Originally defined in the kernel at audit.h | ||
| const ( | ||
| AuditStatusEnabled AuditStatusMask = 1 << iota | ||
| AuditStatusFailure | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.