mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 10:09:05 +02:00
pdh: add slack to counter array buffers for Windows Server 2012 R2 (#2615)
Signed-off-by: Alexandre Possebom <alexandrepossebom@gmail.com>
This commit is contained in:
+30
-16
@@ -289,6 +289,13 @@ func (c *Collector) Collect(dst any) error {
|
||||
return <-c.errorCh
|
||||
}
|
||||
|
||||
// pdhBufferSlack is extra space allocated beyond the buffer size reported by PDH.
|
||||
// On Windows Server 2012 R2, PdhGetRawCounterArrayW reports the size of the item array of a single-instance
|
||||
// object (e.g. System) without the terminator of the empty instance name, and writes that terminator right after
|
||||
// the array: 48 bytes reported, 2 bytes written past them. Without the slack, the write corrupts the neighboring
|
||||
// heap object and SzName points into it, which the GC reports as "found pointer to free object".
|
||||
const pdhBufferSlack = 8
|
||||
|
||||
func (c *Collector) collectWorkerRaw() {
|
||||
var (
|
||||
err error
|
||||
@@ -297,7 +304,10 @@ func (c *Collector) collectWorkerRaw() {
|
||||
bytesNeeded uint32
|
||||
)
|
||||
|
||||
buf := make([]byte, 1)
|
||||
// buf starts empty so that the first call only queries the required size with a nil buffer.
|
||||
// PdhGetRawCounterArrayW writes 8 bytes into a non-nil buffer, even if lpdwBufferSize is smaller,
|
||||
// which corrupts the neighboring heap memory of a tiny allocation.
|
||||
var buf []byte
|
||||
|
||||
for data := range c.collectCh {
|
||||
err = (func() error {
|
||||
@@ -334,12 +344,13 @@ func (c *Collector) collectWorkerRaw() {
|
||||
stringMap := map[*uint16]string{}
|
||||
|
||||
for _, counter := range c.counters {
|
||||
instances:
|
||||
for _, instance := range counter.Instances {
|
||||
// Get the info with the current buffer size
|
||||
bytesNeeded = uint32(cap(buf))
|
||||
bytesNeeded = uint32(len(buf))
|
||||
|
||||
for {
|
||||
ret := GetRawCounterArray(instance, &bytesNeeded, &itemCount, &buf[0])
|
||||
ret := GetRawCounterArray(instance, &bytesNeeded, &itemCount, unsafe.SliceData(buf))
|
||||
|
||||
if ret == ErrorSuccess {
|
||||
break
|
||||
@@ -347,20 +358,20 @@ func (c *Collector) collectWorkerRaw() {
|
||||
|
||||
if err := NewPdhError(ret); ret != MoreData {
|
||||
if isKnownCounterDataError(err) {
|
||||
break
|
||||
continue instances
|
||||
}
|
||||
|
||||
return fmt.Errorf("GetRawCounterArray: %w", err)
|
||||
}
|
||||
|
||||
if bytesNeeded <= uint32(cap(buf)) {
|
||||
return fmt.Errorf("GetRawCounterArray reports buffer too small (%d), but buffer is large enough (%d): %w", uint32(cap(buf)), bytesNeeded, NewPdhError(ret))
|
||||
if bytesNeeded <= uint32(len(buf)) {
|
||||
return fmt.Errorf("GetRawCounterArray reports buffer too small (%d), but buffer is large enough (%d): %w", uint32(len(buf)), bytesNeeded, NewPdhError(ret))
|
||||
}
|
||||
|
||||
buf = make([]byte, bytesNeeded)
|
||||
buf = make([]byte, bytesNeeded+pdhBufferSlack)
|
||||
}
|
||||
|
||||
items = unsafe.Slice((*RawCounterItem)(unsafe.Pointer(&buf[0])), itemCount)
|
||||
items = unsafe.Slice((*RawCounterItem)(unsafe.Pointer(unsafe.SliceData(buf))), itemCount)
|
||||
|
||||
var (
|
||||
instanceName string
|
||||
@@ -458,7 +469,9 @@ func (c *Collector) collectWorkerFormatted() {
|
||||
bytesNeeded uint32
|
||||
)
|
||||
|
||||
buf := make([]byte, 1)
|
||||
// buf starts empty so that the first call only queries the required size with a nil buffer.
|
||||
// See collectWorkerRaw.
|
||||
var buf []byte
|
||||
|
||||
for data := range c.collectCh {
|
||||
err = (func() error {
|
||||
@@ -495,12 +508,13 @@ func (c *Collector) collectWorkerFormatted() {
|
||||
stringMap := map[*uint16]string{}
|
||||
|
||||
for _, counter := range c.counters {
|
||||
instances:
|
||||
for _, instance := range counter.Instances {
|
||||
// Get the info with the current buffer size
|
||||
bytesNeeded = uint32(cap(buf))
|
||||
bytesNeeded = uint32(len(buf))
|
||||
|
||||
for {
|
||||
ret := GetFormattedCounterArrayDouble(instance, &bytesNeeded, &itemCount, &buf[0])
|
||||
ret := GetFormattedCounterArrayDouble(instance, &bytesNeeded, &itemCount, unsafe.SliceData(buf))
|
||||
|
||||
if ret == ErrorSuccess {
|
||||
break
|
||||
@@ -508,20 +522,20 @@ func (c *Collector) collectWorkerFormatted() {
|
||||
|
||||
if err := NewPdhError(ret); ret != MoreData {
|
||||
if isKnownCounterDataError(err) {
|
||||
break
|
||||
continue instances
|
||||
}
|
||||
|
||||
return fmt.Errorf("GetFormattedCounterArrayDouble: %w", err)
|
||||
}
|
||||
|
||||
if bytesNeeded <= uint32(cap(buf)) {
|
||||
return fmt.Errorf("GetFormattedCounterArrayDouble reports buffer too small (%d), but buffer is large enough (%d): %w", uint32(cap(buf)), bytesNeeded, NewPdhError(ret))
|
||||
if bytesNeeded <= uint32(len(buf)) {
|
||||
return fmt.Errorf("GetFormattedCounterArrayDouble reports buffer too small (%d), but buffer is large enough (%d): %w", uint32(len(buf)), bytesNeeded, NewPdhError(ret))
|
||||
}
|
||||
|
||||
buf = make([]byte, bytesNeeded)
|
||||
buf = make([]byte, bytesNeeded+pdhBufferSlack)
|
||||
}
|
||||
|
||||
items = unsafe.Slice((*FmtCounterValueItemDouble)(unsafe.Pointer(&buf[0])), itemCount)
|
||||
items = unsafe.Slice((*FmtCounterValueItemDouble)(unsafe.Pointer(unsafe.SliceData(buf))), itemCount)
|
||||
|
||||
var (
|
||||
instanceName string
|
||||
|
||||
Reference in New Issue
Block a user