mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-08-28 04:46:50 -04:00
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:
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package etw
|
||||
|
||||
+7
-3
@@ -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:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// +build windows
|
||||
// +build arm
|
||||
//go:build windows && arm
|
||||
// +build windows,arm
|
||||
|
||||
package etw
|
||||
|
||||
|
||||
+52
-24
@@ -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,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package etw
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build windows && (386 || arm)
|
||||
// +build windows
|
||||
// +build 386 arm
|
||||
|
||||
package etw
|
||||
|
||||
@@ -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.
|
||||
@@ -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,3 +1,4 @@
|
||||
//go:build windows && (386 || arm)
|
||||
// +build windows
|
||||
// +build 386 arm
|
||||
|
||||
|
||||
+17
-4
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package etwlogrus
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user