Make it easy to create a zeroed buffer via calloc(), preventing leaking
sensitive info from the heap.

Benchmarking shows that creating a zeroed buffer is much slower compared
with uninitialized buffer, but much faster compared with manually
initializing the buffer with a loop.

BenchmarkMakeAioBuffer-12                7252674               148.1 ns/op
BenchmarkMakeAioBufferZero-12             262107              4181 ns/op
BenchmarkAioBufferZero-12                  17581             68759 ns/op

It is interesting that creating a zeroed buffer is 3 times faster
compared with making a new []byte slice:

BenchmarkMakeAioBufferZero-12             247710              4440 ns/op
BenchmarkMakeByteSlice-12                  84117             13733 ns/op

Signed-off-by: Nir Soffer <nsof...@redhat.com>
---
 golang/aio_buffer.go                 |  6 ++++++
 golang/libnbd_020_aio_buffer_test.go | 16 ++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/golang/aio_buffer.go b/golang/aio_buffer.go
index 2cd8ceb2..d2e6e350 100644
--- a/golang/aio_buffer.go
+++ b/golang/aio_buffer.go
@@ -38,20 +38,26 @@ type AioBuffer struct {
        P    unsafe.Pointer
        Size uint
 }
 
 // MakeAioBuffer makes a new buffer backed by an uninitialized C allocated
 // array.
 func MakeAioBuffer(size uint) AioBuffer {
        return AioBuffer{C.malloc(C.ulong(size)), size}
 }
 
+// MakeAioBuffer makes a new buffer backed by a C allocated array. The
+// underlying buffer is set to zero.
+func MakeAioBufferZero(size uint) AioBuffer {
+       return AioBuffer{C.calloc(C.ulong(1), C.ulong(size)), size}
+}
+
 // FromBytes makes a new buffer backed by a C allocated array, initialized by
 // copying the given Go slice.
 func FromBytes(buf []byte) AioBuffer {
        size := len(buf)
        ret := MakeAioBuffer(uint(size))
        for i := 0; i < len(buf); i++ {
                *ret.Get(uint(i)) = buf[i]
        }
        return ret
 }
diff --git a/golang/libnbd_020_aio_buffer_test.go 
b/golang/libnbd_020_aio_buffer_test.go
index cec74ddc..b3a2a8d9 100644
--- a/golang/libnbd_020_aio_buffer_test.go
+++ b/golang/libnbd_020_aio_buffer_test.go
@@ -51,20 +51,28 @@ func TestAioBuffer(t *testing.T) {
                t.Fatalf("Expected %v, got %v", zeroes, buf.Bytes())
        }
 
        /* Create another buffer from Go slice. */
        buf2 := FromBytes(zeroes)
        defer buf2.Free()
 
        if !bytes.Equal(buf2.Bytes(), zeroes) {
                t.Fatalf("Expected %v, got %v", zeroes, buf2.Bytes())
        }
+
+       /* Crated a zeroed buffer. */
+       buf3 := MakeAioBufferZero(uint(32))
+       defer buf.Free()
+
+       if !bytes.Equal(buf3.Bytes(), zeroes) {
+               t.Fatalf("Expected %v, got %v", zeroes, buf2.Bytes())
+       }
 }
 
 func TestAioBufferFree(t *testing.T) {
        buf := MakeAioBuffer(uint(32))
 
        /* Free the underlying C array. */
        buf.Free()
 
        /* And clear the pointer. */
        if buf.P != nil {
@@ -105,20 +113,28 @@ func TestAioBufferGetAfterFree(t *testing.T) {
 const bufferSize uint = 256 * 1024
 
 // Benchmark creating an uninitialized buffer.
 func BenchmarkMakeAioBuffer(b *testing.B) {
        for i := 0; i < b.N; i++ {
                buf := MakeAioBuffer(bufferSize)
                buf.Free()
        }
 }
 
+// Benchmark creating zeroed buffer.
+func BenchmarkMakeAioBufferZero(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               buf := MakeAioBufferZero(bufferSize)
+               buf.Free()
+       }
+}
+
 // Benchmark zeroing a buffer.
 func BenchmarkAioBufferZero(b *testing.B) {
        for i := 0; i < b.N; i++ {
                buf := MakeAioBuffer(bufferSize)
                for i := uint(0); i < bufferSize; i++ {
                        *buf.Get(i) = 0
                }
                buf.Free()
        }
 }
-- 
2.34.1

_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to