Skip to content

Commit 101bb99

Browse files
committed
Fix memfd write clobber on EEXIST in faultPageViaMemfdWake
Read source data into temporary buffer before calling UFFDIO_WAKE. Only copy to memfd backing store after wake succeeds, preserving first-writer-wins semantics when EEXIST is returned.
1 parent 3bedd2f commit 101bb99

1 file changed

Lines changed: 4 additions & 5 deletions

File tree

‎packages/orchestrator/pkg/sandbox/uffd/userfaultfd/missing_wake.go‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ func (u *Userfaultfd) faultPageViaMemfdWake(
6868
return faultDiscarded, errors.Join(err, safeInvoke(onFailure))
6969
}
7070

71-
page := dst[offset : offset+pageSize]
71+
tmpBuf := make([]byte, pageSize)
7272

7373
var dataErr error
7474
var attempt int
7575

7676
retryLoop:
7777
for attempt = range sliceMaxRetries + 1 {
7878
var n int
79-
n, dataErr = source.ReadAt(ctx, page, offset)
79+
n, dataErr = source.ReadAt(ctx, tmpBuf, offset)
8080
if dataErr == nil && int64(n) != pageSize {
8181
dataErr = fmt.Errorf("short read at %d: got %d, want %d", offset, n, pageSize)
8282
}
@@ -115,9 +115,6 @@ retryLoop:
115115
}
116116

117117
if err := u.fd.wake(addr, u.pageSize); err != nil {
118-
// EEXIST: page already installed by a concurrent path. The memfd
119-
// write is idempotent (same source for on-demand and prefault),
120-
// matching UFFDIO_COPY's first-writer-wins semantics.
121118
if errors.Is(err, unix.EEXIST) {
122119
span.SetAttributes(attribute.Bool("uffd.already_mapped", true))
123120

@@ -139,5 +136,7 @@ retryLoop:
139136
return faultDiscarded, fmt.Errorf("UFFDIO_WAKE: %w", joined)
140137
}
141138

139+
copy(dst[offset:offset+pageSize], tmpBuf)
140+
142141
return faultInstalled, nil
143142
}

0 commit comments

Comments
 (0)