Add lint and go generate steps to CI (#254)

* Add lint and go generate stages to CI

Add CI step to verify `go generate` was run on repo.
Add linter stage to CI along with linter config file,
`.golangci.yml`.
Will likely prefer revive over static-check.

Updated README Contributing section on linting requirements.

Added sequence ordering to make sure lint and go generate stages run
before tests and build.
This way, build and tests are not run on code that could potentially:

    1. not build due to `gofmt` issues;
    2. contain bugs;
    3. have to be re-submitted after issues are fixed; or
    4. contain outdated Win32 syscall or other auto-generated files.

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>

* Fixed linter issues

Code changes to satisfy linters:

 - Ran `gofmt -s -w` on repo.
 - Broke up long lines.
 - When possible, changed names with incorrect initialism formatting
   - Added exceptions for exported variables.
 - Added exceptions for ALL_CAPS_WITH_UNDERSCORES code.
   - Switched to using `windows` or `syscall` definitions if possible;
     especially if some constants were unused.
 - Added `_ =` to satisfy error linter, and acknowledge that errors are
   being ignored.
 - Switched to using `errors.Is` and `As` in places, elsewhere added
   exceptions if error value was known to be `syscall.Errno`.
 - Removed bare returns.
 - Prevented variables from being overshadowed in certain places
   (ignoring cases of overshadowing `err`).
 - Renamed variables and functions (eg, `len`, `eventMetadata.bytes`) to
   prevent shadowing pre-built functions and imported pacakges.
 - Removed unused method receivers.
 - Added exceptions to certain unused (unexported) constants and
   functions.
   - Deleted unused `once` from `pkg/etw.providerMap`.
 - Renamed `noop.go` files to `main_other.go` or `doc.go`, to better fit
   style recommendations.
 - Added exceptions for non-secure use of SHA1 and weak crypto
   libraries.
 - Replaced `ioutil` with `io` and `os` (and `t.TempDir` in tests).
 - Added fully exhaustive checks for `switch` statements in `pkg/etw`.
 - Defined constant strings for `tools/mkwinsyscall`.
 - Removed unnecessary conversions.
 - Made sure `context.Cancel` was called.

Additionally, added `//go:build windows" constraints on files with
unexported code, since linter will complain about unused code on
non-Windows platforms.

Added a stub `main() {}` for `mkwinsyscall` for non-Windows builds, just in
case `//go:generate` directives are added to OS-agnostic files.

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>

* PR: spelling, constants, fuzzing

Moved HVSocket fuzzing tests to separate file with go 1.18 build
constraint.

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>
This commit is contained in:
Hamza El-Saawy
2022-08-23 15:05:05 -04:00
committed by GitHub
parent 79ae8cea02
commit e268c11e27
67 changed files with 1192 additions and 674 deletions
+8
View File
@@ -0,0 +1,8 @@
// Package etw provides support for TraceLogging-based ETW (Event Tracing
// for Windows). TraceLogging is a format of ETW events that are self-describing
// (the event contains information on its own schema). This allows them to be
// decoded without needing a separate manifest with event information. The
// implementation here is based on the information found in
// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a
// set of C macros.
package etw
+14 -13
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package etw
@@ -14,60 +15,60 @@ type eventData struct {
buffer bytes.Buffer
}
// bytes returns the raw binary data containing the event data. The returned
// toBytes returns the raw binary data containing the event data. The returned
// value is not copied from the internal buffer, so it can be mutated by the
// eventData object after it is returned.
func (ed *eventData) bytes() []byte {
func (ed *eventData) toBytes() []byte {
return ed.buffer.Bytes()
}
// writeString appends a string, including the null terminator, to the buffer.
func (ed *eventData) writeString(data string) {
ed.buffer.WriteString(data)
ed.buffer.WriteByte(0)
_, _ = ed.buffer.WriteString(data)
_ = ed.buffer.WriteByte(0)
}
// writeInt8 appends a int8 to the buffer.
func (ed *eventData) writeInt8(value int8) {
ed.buffer.WriteByte(uint8(value))
_ = ed.buffer.WriteByte(uint8(value))
}
// writeInt16 appends a int16 to the buffer.
func (ed *eventData) writeInt16(value int16) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
// writeInt32 appends a int32 to the buffer.
func (ed *eventData) writeInt32(value int32) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
// writeInt64 appends a int64 to the buffer.
func (ed *eventData) writeInt64(value int64) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
// writeUint8 appends a uint8 to the buffer.
func (ed *eventData) writeUint8(value uint8) {
ed.buffer.WriteByte(value)
_ = ed.buffer.WriteByte(value)
}
// writeUint16 appends a uint16 to the buffer.
func (ed *eventData) writeUint16(value uint16) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
// writeUint32 appends a uint32 to the buffer.
func (ed *eventData) writeUint32(value uint32) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
// writeUint64 appends a uint64 to the buffer.
func (ed *eventData) writeUint64(value uint64) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
// writeFiletime appends a FILETIME to the buffer.
func (ed *eventData) writeFiletime(value syscall.Filetime) {
binary.Write(&ed.buffer, binary.LittleEndian, value)
_ = binary.Write(&ed.buffer, binary.LittleEndian, value)
}
+7 -5
View File
@@ -1,3 +1,5 @@
//go:build windows
package etw
import (
@@ -13,11 +15,11 @@ const (
)
type eventDataDescriptor struct {
ptr ptr64
size uint32
dataType eventDataDescriptorType
reserved1 uint8
reserved2 uint16
ptr ptr64
size uint32
dataType eventDataDescriptorType
_ uint8
_ uint16
}
func newEventDataDescriptor(dataType eventDataDescriptorType, buffer []byte) eventDataDescriptor {
+8
View File
@@ -1,3 +1,5 @@
//go:build windows
package etw
// Channel represents the ETW logging channel that is used. It can be used by
@@ -45,6 +47,8 @@ const (
)
// EventDescriptor represents various metadata for an ETW event.
//
//nolint:structcheck // task is currently unused
type eventDescriptor struct {
id uint16
version uint8
@@ -70,6 +74,8 @@ func newEventDescriptor() *eventDescriptor {
// should uniquely identify the other event metadata (contained in
// EventDescriptor, and field metadata). Only the lower 24 bits of this value
// are relevant.
//
//nolint:unused // keep for future use
func (ed *eventDescriptor) identity() uint32 {
return (uint32(ed.version) << 16) | uint32(ed.id)
}
@@ -78,6 +84,8 @@ func (ed *eventDescriptor) identity() uint32 {
// should uniquely identify the other event metadata (contained in
// EventDescriptor, and field metadata). Only the lower 24 bits of this value
// are relevant.
//
//nolint:unused // keep for future use
func (ed *eventDescriptor) setIdentity(identity uint32) {
ed.id = uint16(identity)
ed.version = uint8(identity >> 16)
+17 -5
View File
@@ -1,3 +1,5 @@
//go:build windows
package etw
import (
@@ -10,6 +12,8 @@ type inType byte
// Various inType definitions for TraceLogging. These must match the definitions
// found in TraceLoggingProvider.h in the Windows SDK.
//
//nolint:deadcode,varcheck // keep unused constants for potential future use
const (
inTypeNull inType = iota
inTypeUnicodeString
@@ -47,6 +51,8 @@ type outType byte
// Various outType definitions for TraceLogging. These must match the
// definitions found in TraceLoggingProvider.h in the Windows SDK.
//
//nolint:deadcode,varcheck // keep unused constants for potential future use
const (
// outTypeDefault indicates that the default formatting for the inType will
// be used by the event decoder.
@@ -81,11 +87,11 @@ type eventMetadata struct {
buffer bytes.Buffer
}
// bytes returns the raw binary data containing the event metadata. Before being
// toBytes returns the raw binary data containing the event metadata. Before being
// returned, the current size of the buffer is written to the start of the
// buffer. The returned value is not copied from the internal buffer, so it can
// be mutated by the eventMetadata object after it is returned.
func (em *eventMetadata) bytes() []byte {
func (em *eventMetadata) toBytes() []byte {
// Finalize the event metadata buffer by filling in the buffer length at the
// beginning.
binary.LittleEndian.PutUint16(em.buffer.Bytes(), uint16(em.buffer.Len()))
@@ -95,7 +101,7 @@ func (em *eventMetadata) bytes() []byte {
// writeEventHeader writes the metadata for the start of an event to the buffer.
// This specifies the event name and tags.
func (em *eventMetadata) writeEventHeader(name string, tags uint32) {
binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder
_ = binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder
em.writeTags(tags)
em.buffer.WriteString(name)
em.buffer.WriteByte(0) // Null terminator for name
@@ -118,7 +124,7 @@ func (em *eventMetadata) writeFieldInner(name string, inType inType, outType out
}
if arrSize != 0 {
binary.Write(&em.buffer, binary.LittleEndian, arrSize)
_ = binary.Write(&em.buffer, binary.LittleEndian, arrSize)
}
}
@@ -151,13 +157,17 @@ func (em *eventMetadata) writeTags(tags uint32) {
}
// writeField writes the metadata for a simple field to the buffer.
//
//nolint:unparam // tags is currently always 0, may change in the future
func (em *eventMetadata) writeField(name string, inType inType, outType outType, tags uint32) {
em.writeFieldInner(name, inType, outType, tags, 0)
}
// writeArray writes the metadata for an array field to the buffer. The number
// of elements in the array must be written as a uint16 in the event data,
// immediately preceeding the event data.
// immediately preceding the event data.
//
//nolint:unparam // tags is currently always 0, may change in the future
func (em *eventMetadata) writeArray(name string, inType inType, outType outType, tags uint32) {
em.writeFieldInner(name, inType|inTypeArray, outType, tags, 0)
}
@@ -165,6 +175,8 @@ func (em *eventMetadata) writeArray(name string, inType inType, outType outType,
// writeCountedArray writes the metadata for an array field to the buffer. The
// size of a counted array is fixed, and the size is written into the metadata
// directly.
//
//nolint:unused // keep for future use
func (em *eventMetadata) writeCountedArray(name string, count uint16, inType inType, outType outType, tags uint32) {
em.writeFieldInner(name, inType|inTypeCountedArray, outType, tags, count)
}
+1
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package etw
+7 -3
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package etw
@@ -481,7 +482,7 @@ func SmartField(name string, v interface{}) FieldOpt {
case reflect.Int32:
return SmartField(name, int32(rv.Int()))
case reflect.Int64:
return SmartField(name, int64(rv.Int()))
return SmartField(name, int64(rv.Int())) //nolint:unconvert // make look consistent
case reflect.Uint:
return SmartField(name, uint(rv.Uint()))
case reflect.Uint8:
@@ -491,13 +492,13 @@ func SmartField(name string, v interface{}) FieldOpt {
case reflect.Uint32:
return SmartField(name, uint32(rv.Uint()))
case reflect.Uint64:
return SmartField(name, uint64(rv.Uint()))
return SmartField(name, uint64(rv.Uint())) //nolint:unconvert // make look consistent
case reflect.Uintptr:
return SmartField(name, uintptr(rv.Uint()))
case reflect.Float32:
return SmartField(name, float32(rv.Float()))
case reflect.Float64:
return SmartField(name, float64(rv.Float()))
return SmartField(name, float64(rv.Float())) //nolint:unconvert // make look consistent
case reflect.String:
return SmartField(name, rv.String())
case reflect.Struct:
@@ -509,6 +510,9 @@ func SmartField(name string, v interface{}) FieldOpt {
}
}
return Struct(name, fields...)
case reflect.Array, reflect.Chan, reflect.Complex128, reflect.Complex64,
reflect.Func, reflect.Interface, reflect.Invalid, reflect.Map, reflect.Ptr,
reflect.Slice, reflect.UnsafePointer:
}
}
+8 -7
View File
@@ -1,3 +1,4 @@
//go:build windows && (amd64 || arm64 || 386)
// +build windows
// +build amd64 arm64 386
@@ -45,18 +46,18 @@ func NewProviderWithOptions(name string, options ...ProviderOpt) (provider *Prov
trait := &bytes.Buffer{}
if opts.group != (guid.GUID{}) {
binary.Write(trait, binary.LittleEndian, uint16(0)) // Write empty size for buffer (update later)
binary.Write(trait, binary.LittleEndian, uint8(1)) // EtwProviderTraitTypeGroup
traitArray := opts.group.ToWindowsArray() // Append group guid
_ = binary.Write(trait, binary.LittleEndian, uint16(0)) // Write empty size for buffer (update later)
_ = binary.Write(trait, binary.LittleEndian, uint8(1)) // EtwProviderTraitTypeGroup
traitArray := opts.group.ToWindowsArray() // Append group guid
trait.Write(traitArray[:])
binary.LittleEndian.PutUint16(trait.Bytes(), uint16(trait.Len())) // Update size
}
metadata := &bytes.Buffer{}
binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later)
_ = binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later)
metadata.WriteString(name)
metadata.WriteByte(0) // Null terminator for name
trait.WriteTo(metadata) // Add traits if applicable
_, _ = trait.WriteTo(metadata) // Add traits if applicable
binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer
provider.metadata = metadata.Bytes()
@@ -64,8 +65,8 @@ func NewProviderWithOptions(name string, options ...ProviderOpt) (provider *Prov
provider.handle,
eventInfoClassProviderSetTraits,
uintptr(unsafe.Pointer(&provider.metadata[0])),
uint32(len(provider.metadata))); err != nil {
uint32(len(provider.metadata)),
); err != nil {
return nil, err
}
+2 -2
View File
@@ -1,5 +1,5 @@
// +build windows
// +build arm
//go:build windows && arm
// +build windows,arm
package etw
+52 -24
View File
@@ -1,9 +1,10 @@
//go:build windows
// +build windows
package etw
import (
"crypto/sha1"
"crypto/sha1" //nolint:gosec // not used for secure application
"encoding/binary"
"strings"
"unicode/utf16"
@@ -27,7 +28,7 @@ type Provider struct {
keywordAll uint64
}
// String returns the `provider`.ID as a string
// String returns the `provider`.ID as a string.
func (provider *Provider) String() string {
if provider == nil {
return "<nil>"
@@ -54,6 +55,7 @@ const (
type eventInfoClass uint32
//nolint:deadcode,varcheck // keep unused constants for potential future use
const (
eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota
eventInfoClassProviderSetReserved1
@@ -65,10 +67,19 @@ const (
// enable/disable notifications from ETW.
type EnableCallback func(guid.GUID, ProviderState, Level, uint64, uint64, uintptr)
func providerCallback(sourceID guid.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
func providerCallback(
sourceID guid.GUID,
state ProviderState,
level Level,
matchAnyKeyword uint64,
matchAllKeyword uint64,
filterData uintptr,
i uintptr,
) {
provider := providers.getProvider(uint(i))
switch state {
case ProviderStateCaptureState:
case ProviderStateDisable:
provider.enabled = false
case ProviderStateEnable:
@@ -90,17 +101,22 @@ func providerCallback(sourceID guid.GUID, state ProviderState, level Level, matc
//
// The algorithm is roughly the RFC 4122 algorithm for a V5 UUID, but differs in
// the following ways:
// - The input name is first upper-cased, UTF16-encoded, and converted to
// big-endian.
// - No variant is set on the result UUID.
// - The result UUID is treated as being in little-endian format, rather than
// big-endian.
// - The input name is first upper-cased, UTF16-encoded, and converted to
// big-endian.
// - No variant is set on the result UUID.
// - The result UUID is treated as being in little-endian format, rather than
// big-endian.
func providerIDFromName(name string) guid.GUID {
buffer := sha1.New()
namespace := guid.GUID{0x482C2DB2, 0xC390, 0x47C8, [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}}
buffer := sha1.New() //nolint:gosec // not used for secure application
namespace := guid.GUID{
Data1: 0x482C2DB2,
Data2: 0xC390,
Data3: 0x47C8,
Data4: [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB},
}
namespaceBytes := namespace.ToArray()
buffer.Write(namespaceBytes[:])
binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
_ = binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
sum := buffer.Sum(nil)
sum[7] = (sum[7] & 0xf) | 0x50
@@ -117,25 +133,24 @@ type providerOpts struct {
}
// ProviderOpt allows the caller to specify provider options to
// NewProviderWithOptions
// NewProviderWithOptions.
type ProviderOpt func(*providerOpts)
// WithCallback is used to provide a callback option to NewProviderWithOptions
// WithCallback is used to provide a callback option to NewProviderWithOptions.
func WithCallback(callback EnableCallback) ProviderOpt {
return func(opts *providerOpts) {
opts.callback = callback
}
}
// WithID is used to provide a provider ID option to NewProviderWithOptions
// WithID is used to provide a provider ID option to NewProviderWithOptions.
func WithID(id guid.GUID) ProviderOpt {
return func(opts *providerOpts) {
opts.id = id
}
}
// WithGroup is used to provide a provider group option to
// NewProviderWithOptions
// WithGroup is used to provide a provider group option to NewProviderWithOptions.
func WithGroup(group guid.GUID) ProviderOpt {
return func(opts *providerOpts) {
opts.group = group
@@ -237,11 +252,17 @@ func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpt
// event metadata (e.g. for the name) so we don't need to do this check for
// the metadata.
dataBlobs := [][]byte{}
if len(ed.bytes()) > 0 {
dataBlobs = [][]byte{ed.bytes()}
if len(ed.toBytes()) > 0 {
dataBlobs = [][]byte{ed.toBytes()}
}
return provider.writeEventRaw(options.descriptor, options.activityID, options.relatedActivityID, [][]byte{em.bytes()}, dataBlobs)
return provider.writeEventRaw(
options.descriptor,
options.activityID,
options.relatedActivityID,
[][]byte{em.toBytes()},
dataBlobs,
)
}
// writeEventRaw writes a single ETW event from the provider. This function is
@@ -257,17 +278,24 @@ func (provider *Provider) writeEventRaw(
relatedActivityID guid.GUID,
metadataBlobs [][]byte,
dataBlobs [][]byte) error {
dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs))
dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount)
dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata))
dataDescriptors = append(dataDescriptors,
newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata))
for _, blob := range metadataBlobs {
dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob))
dataDescriptors = append(dataDescriptors,
newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob))
}
for _, blob := range dataBlobs {
dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
dataDescriptors = append(dataDescriptors,
newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
}
return eventWriteTransfer(provider.handle, descriptor, (*windows.GUID)(&activityID), (*windows.GUID)(&relatedActivityID), dataDescriptorCount, &dataDescriptors[0])
return eventWriteTransfer(provider.handle,
descriptor,
(*windows.GUID)(&activityID),
(*windows.GUID)(&relatedActivityID),
dataDescriptorCount,
&dataDescriptors[0])
}
+1
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package etw
+3 -1
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package etw
@@ -14,7 +15,6 @@ type providerMap struct {
m map[uint]*Provider
i uint
lock sync.Mutex
once sync.Once
}
var providers = providerMap{
@@ -50,5 +50,7 @@ func (p *providerMap) getProvider(index uint) *Provider {
return p.m[index]
}
//todo: combine these into struct, so that "globalProviderCallback" is guaranteed to be initialized through method access
var providerCallbackOnce sync.Once
var globalProviderCallback uintptr
+2
View File
@@ -1,3 +1,5 @@
//go:build windows && (386 || arm)
// +build windows
// +build 386 arm
package etw
+2
View File
@@ -1,3 +1,5 @@
//go:build windows && (amd64 || arm64)
// +build windows
// +build amd64 arm64
package etw
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package main
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
// Shows a sample usage of the ETW logging package.
+3 -8
View File
@@ -1,13 +1,8 @@
// Package etw provides support for TraceLogging-based ETW (Event Tracing
// for Windows). TraceLogging is a format of ETW events that are self-describing
// (the event contains information on its own schema). This allows them to be
// decoded without needing a separate manifest with event information. The
// implementation here is based on the information found in
// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a
// set of C macros.
//go:build windows
package etw
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go etw.go
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go syscall.go
//sys eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) = advapi32.EventRegister
+1
View File
@@ -1,3 +1,4 @@
//go:build windows && (386 || arm)
// +build windows
// +build 386 arm
+17 -4
View File
@@ -1,3 +1,4 @@
//go:build windows && (amd64 || arm64)
// +build windows
// +build amd64 arm64
@@ -19,7 +20,6 @@ func eventWriteTransfer(
relatedActivityID *windows.GUID,
dataDescriptorCount uint32,
dataDescriptors *eventDataDescriptor) (win32err error) {
return eventWriteTransfer_64(
providerHandle,
descriptor,
@@ -34,7 +34,6 @@ func eventSetInformation(
class eventInfoClass,
information uintptr,
length uint32) (win32err error) {
return eventSetInformation_64(
providerHandle,
class,
@@ -46,7 +45,21 @@ func eventSetInformation(
// for provider notifications. Because Go has trouble with callback arguments of
// different size, it has only pointer-sized arguments, which are then cast to
// the appropriate types when calling providerCallback.
func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
providerCallback(*sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
func providerCallbackAdapter(
sourceID *guid.GUID,
state uintptr,
level uintptr,
matchAnyKeyword uintptr,
matchAllKeyword uintptr,
filterData uintptr,
i uintptr,
) uintptr {
providerCallback(*sourceID,
ProviderState(state),
Level(level),
uint64(matchAnyKeyword),
uint64(matchAllKeyword),
filterData,
i)
return 0
}
+7 -7
View File
@@ -17,7 +17,7 @@ const defaultEventName = "LogrusEntry"
// ErrNoProvider is returned when a hook is created without a provider being configured.
var ErrNoProvider = errors.New("no ETW registered provider")
// HookOpt is an option to change the behavior of the Logrus ETW hook
// HookOpt is an option to change the behavior of the Logrus ETW hook.
type HookOpt func(*Hook) error
// Hook is a Logrus hook which logs received events to ETW.
@@ -30,16 +30,16 @@ type Hook struct {
getEventsOpts func(*logrus.Entry) []etw.EventOpt
}
// NewHook registers a new ETW provider and returns a hook to log from it. The
// provider will be closed when the hook is closed.
// NewHook registers a new ETW provider and returns a hook to log from it.
// The provider will be closed when the hook is closed.
func NewHook(providerName string, opts ...HookOpt) (*Hook, error) {
opts = append(opts, WithNewETWProvider(providerName))
return NewHookFromOpts(opts...)
}
// NewHookFromProvider creates a new hook based on an existing ETW provider. The
// provider will not be closed when the hook is closed.
// NewHookFromProvider creates a new hook based on an existing ETW provider.
// The provider will not be closed when the hook is closed.
func NewHookFromProvider(provider *etw.Provider, opts ...HookOpt) (*Hook, error) {
opts = append(opts, WithExistingETWProvider(provider))
@@ -73,7 +73,7 @@ func (h *Hook) validate() error {
// Levels returns the set of levels that this hook wants to receive log entries
// for.
func (h *Hook) Levels() []logrus.Level {
func (*Hook) Levels() []logrus.Level {
return logrus.AllLevels
}
@@ -142,7 +142,7 @@ func (h *Hook) Fire(e *logrus.Entry) error {
// as a session listening for the event having no available space in its
// buffers). Therefore, we don't return the error from WriteEvent, as it is
// just noise in many cases.
h.provider.WriteEvent(name, opts, fields)
_ = h.provider.WriteEvent(name, opts, fields)
return nil
}
+1
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package etwlogrus
+1 -1
View File
@@ -44,7 +44,7 @@ func WithGetName(f func(*logrus.Entry) string) HookOpt {
}
}
// WithAdditionalEventOpts allows additional ETW event properties (keywords, tags, etc.) to be specified
// WithEventOpts allows additional ETW event properties (keywords, tags, etc.) to be specified.
func WithEventOpts(f func(*logrus.Entry) []etw.EventOpt) HookOpt {
return func(h *Hook) error {
h.getEventsOpts = f
+1 -1
View File
@@ -27,5 +27,5 @@ func GetFileSystemType(path string) (fsType string, err error) {
drive += `\`
err = windows.GetVolumeInformation(windows.StringToUTF16Ptr(drive), nil, 0, nil, nil, nil, &buf[0], size)
fsType = windows.UTF16ToString(buf)
return
return fsType, err
}
+2 -1
View File
@@ -1,6 +1,7 @@
package fs
import (
"errors"
"os"
"testing"
)
@@ -18,7 +19,7 @@ func TestGetFSTypeOfKnownDrive(t *testing.T) {
func TestGetFSTypeOfInvalidPath(t *testing.T) {
_, err := GetFileSystemType("7:\\")
if err != ErrInvalidPath {
if !errors.Is(err, ErrInvalidPath) {
t.Fatalf("Expected `ErrInvalidPath`, got %v", err)
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ package guid
import (
"crypto/rand"
"crypto/sha1"
"crypto/sha1" //nolint:gosec // not used for secure application
"encoding"
"encoding/binary"
"fmt"
@@ -59,7 +59,7 @@ func NewV4() (GUID, error) {
// big-endian UTF16 stream of bytes. If that is desired, the string can be
// encoded as such before being passed to this function.
func NewV5(namespace GUID, name []byte) (GUID, error) {
b := sha1.New()
b := sha1.New() //nolint:gosec // not used for secure application
namespaceBytes := namespace.ToArray()
b.Write(namespaceBytes[:])
b.Write(name)
+4 -2
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package process
@@ -32,6 +33,8 @@ func EnumProcesses() ([]uint32, error) {
// ProcessMemoryCountersEx is the PROCESS_MEMORY_COUNTERS_EX struct from
// Windows:
// https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters_ex
//
//nolint:revive // process.ProcessMemoryCountersEx stutters, too late to change it
type ProcessMemoryCountersEx struct {
Cb uint32
PageFaultCount uint32
@@ -75,7 +78,7 @@ func QueryFullProcessImageName(process windows.Handle, flags uint32) (string, er
for {
b := make([]uint16, bufferSize)
err := queryFullProcessImageName(process, flags, &b[0], &bufferSize)
if err == windows.ERROR_INSUFFICIENT_BUFFER {
if err == windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
bufferSize = bufferSize * 2
continue
}
@@ -84,5 +87,4 @@ func QueryFullProcessImageName(process windows.Handle, flags uint32) (string, er
}
return windows.UTF16ToString(b[:bufferSize]), nil
}
}
+21 -12
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package security
@@ -20,6 +21,7 @@ type (
trusteeForm uint32
trusteeType uint32
//nolint:structcheck // structcheck thinks fields are unused, but the are used to pass data to OS
explicitAccess struct {
accessPermissions accessMask
accessMode accessMode
@@ -27,6 +29,7 @@ type (
trustee trustee
}
//nolint:structcheck,unused // structcheck thinks fields are unused, but the are used to pass data to OS
trustee struct {
multipleTrustee *trustee
multipleTrusteeOperation int32
@@ -44,6 +47,7 @@ const (
desiredAccessReadControl desiredAccess = 0x20000
desiredAccessWriteDac desiredAccess = 0x40000
//cspell:disable-next-line
gvmga = "GrantVmGroupAccess:"
inheritModeNoInheritance inheritMode = 0x0
@@ -56,9 +60,9 @@ const (
shareModeRead shareMode = 0x1
shareModeWrite shareMode = 0x2
sidVmGroup = "S-1-5-83-0"
sidVMGroup = "S-1-5-83-0"
trusteeFormIsSid trusteeForm = 0
trusteeFormIsSID trusteeForm = 0
trusteeTypeWellKnownGroup trusteeType = 5
)
@@ -67,6 +71,8 @@ const (
// include Grant ACE entries for the VM Group SID. This is a golang re-
// implementation of the same function in vmcompute, just not exported in
// RS5. Which kind of sucks. Sucks a lot :/
//
//revive:disable-next-line:var-naming VM, not Vm
func GrantVmGroupAccess(name string) error {
// Stat (to determine if `name` is a directory).
s, err := os.Stat(name)
@@ -79,7 +85,7 @@ func GrantVmGroupAccess(name string) error {
if err != nil {
return err // Already wrapped
}
defer syscall.CloseHandle(fd)
defer syscall.CloseHandle(fd) //nolint:errcheck
// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
ot := objectTypeFileObject
@@ -89,7 +95,7 @@ func GrantVmGroupAccess(name string) error {
if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err)
}
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd)))
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) //nolint:errcheck
// Generate a new DACL which is the current DACL with the required ACEs added.
// Must defer LocalFree on success.
@@ -97,7 +103,7 @@ func GrantVmGroupAccess(name string) error {
if err != nil {
return err // Already wrapped
}
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL)))
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) //nolint:errcheck
// And finally use SetSecurityInfo to apply the updated DACL.
if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
@@ -110,16 +116,19 @@ func GrantVmGroupAccess(name string) error {
// createFile is a helper function to call [Nt]CreateFile to get a handle to
// the file or directory.
func createFile(name string, isDir bool) (syscall.Handle, error) {
namep := syscall.StringToUTF16(name)
namep, err := syscall.UTF16FromString(name)
if err != nil {
return syscall.InvalidHandle, fmt.Errorf("could not convernt name to UTF-16: %w", err)
}
da := uint32(desiredAccessReadControl | desiredAccessWriteDac)
sm := uint32(shareModeRead | shareModeWrite)
fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL)
if isDir {
fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS)
fa |= syscall.FILE_FLAG_BACKUP_SEMANTICS
}
fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0)
if err != nil {
return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err)
return syscall.InvalidHandle, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err)
}
return fd, nil
}
@@ -128,9 +137,9 @@ func createFile(name string, isDir bool) (syscall.Handle, error) {
// The caller is responsible for LocalFree of the returned DACL on success.
func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) {
// Generate pointers to the SIDs based on the string SIDs
sid, err := syscall.StringToSid(sidVmGroup)
sid, err := syscall.StringToSid(sidVMGroup)
if err != nil {
return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err)
return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVMGroup, err)
}
inheritance := inheritModeNoInheritance
@@ -139,12 +148,12 @@ func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintp
}
eaArray := []explicitAccess{
explicitAccess{
{
accessPermissions: accessMaskDesiredPermission,
accessMode: accessModeGrant,
inheritance: inheritance,
trustee: trustee{
trusteeForm: trusteeFormIsSid,
trusteeForm: trusteeFormIsSID,
trusteeType: trusteeTypeWellKnownGroup,
name: uintptr(unsafe.Pointer(sid)),
},
+5 -10
View File
@@ -1,9 +1,9 @@
//+build windows
//go:build windows
// +build windows
package security
import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
@@ -36,7 +36,7 @@ const (
// S-1-5-83-1-3166535780-1122986932-343720105-43916321:(I)(R,W)
func TestGrantVmGroupAccess(t *testing.T) {
f, err := ioutil.TempFile("", "gvmgafile")
f, err := os.CreateTemp("", "gvmgafile")
if err != nil {
t.Fatal(err)
}
@@ -45,16 +45,12 @@ func TestGrantVmGroupAccess(t *testing.T) {
os.Remove(f.Name())
}()
d, err := ioutil.TempDir("", "gvmgadir")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(d)
d := t.TempDir()
find, err := os.Create(filepath.Join(d, "find.txt"))
if err != nil {
t.Fatal(err)
}
defer find.Close()
if err := GrantVmGroupAccess(f.Name()); err != nil {
t.Fatal(err)
@@ -88,7 +84,6 @@ func TestGrantVmGroupAccess(t *testing.T) {
find.Name(),
[]string{`(I)(R)`},
)
}
func verifyVMAccountDACLs(t *testing.T, name string, permissions []string) {