Skip to content

Commit 178debe

Browse files
holimanfjl
andauthored
consensus/ethash: avoid runtime errors due to OOD on mmap writes (#23799)
When we map a file for generating the DAG, we do a simple truncate to e.g. 1Gb. This is fine, even if we have nowhere near 1Gb disk available, as the actual file doesn't take up the full 1Gb, merely a few bytes. When we start generating into it, however, it eventually crashes with a unexpected fault address . This change fixes it (on linux systems) by using the Fallocate syscall, which preallocates suffcient space on disk to avoid that situation. Co-authored-by: Felix Lange <[email protected]>
1 parent 2e8b58f commit 178debe

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

consensus/ethash/ethash.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,16 @@ func memoryMapAndGenerate(path string, size uint64, lock bool, generator func(bu
136136
if err != nil {
137137
return nil, nil, nil, err
138138
}
139-
if err = dump.Truncate(int64(len(dumpMagic))*4 + int64(size)); err != nil {
139+
if err = ensureSize(dump, int64(len(dumpMagic))*4+int64(size)); err != nil {
140+
dump.Close()
141+
os.Remove(temp)
140142
return nil, nil, nil, err
141143
}
142144
// Memory map the file for writing and fill it with the generator
143145
mem, buffer, err := memoryMapFile(dump, true)
144146
if err != nil {
145147
dump.Close()
148+
os.Remove(temp)
146149
return nil, nil, nil, err
147150
}
148151
copy(buffer, dumpMagic)
@@ -358,7 +361,7 @@ func (d *dataset) generate(dir string, limit int, lock bool, test bool) {
358361
if err != nil {
359362
logger.Error("Failed to generate mapped ethash dataset", "err", err)
360363

361-
d.dataset = make([]uint32, dsize/2)
364+
d.dataset = make([]uint32, dsize/4)
362365
generateDataset(d.dataset, d.epoch, cache)
363366
}
364367
// Iterate over all previous instances and delete old ones
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2021 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
//go:build linux
18+
// +build linux
19+
20+
package ethash
21+
22+
import (
23+
"os"
24+
25+
"golang.org/x/sys/unix"
26+
)
27+
28+
// ensureSize expands the file to the given size. This is to prevent runtime
29+
// errors later on, if the underlying file expands beyond the disk capacity,
30+
// even though it ostensibly is already expanded, but due to being sparse
31+
// does not actually occupy the full declared size on disk.
32+
func ensureSize(f *os.File, size int64) error {
33+
// Docs: https://www.man7.org/linux/man-pages/man2/fallocate.2.html
34+
return unix.Fallocate(int(f.Fd()), 0, 0, size)
35+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2021 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
//go:build !linux
18+
// +build !linux
19+
20+
package ethash
21+
22+
import (
23+
"os"
24+
)
25+
26+
// ensureSize expands the file to the given size. This is to prevent runtime
27+
// errors later on, if the underlying file expands beyond the disk capacity,
28+
// even though it ostensibly is already expanded, but due to being sparse
29+
// does not actually occupy the full declared size on disk.
30+
func ensureSize(f *os.File, size int64) error {
31+
// On systems which do not support fallocate, we merely truncate it.
32+
// More robust alternatives would be to
33+
// - Use posix_fallocate, or
34+
// - explicitly fill the file with zeroes.
35+
return f.Truncate(size)
36+
}

0 commit comments

Comments
 (0)