Skip to content

Commit 0a089bf

Browse files
committed
bfd: Use dynamic expansion to support large groups/users
When dumping a process with a massive number of supplementary groups, the `Groups:` line in `/proc/<pid>/status` easily exceeds the hardcoded `BUFSIZE` (4096 bytes). This causes `breadchr()` to abort with "The bfd buffer is too small" Instead of globally increasing `BUFSIZE`—which unnecessarily inflates the memory footprint for all routine checkpoint operations—this patch introduces a dynamic expansion mechanism When `breadchr()` reaches the buffer limit, it dynamically maps an independent, doubled VMA to handle the long text line (capped at 2MB to prevent OOM). This oversized buffer is then safely unmapped during `buf_put()` to avoid polluting the fixed-size zero-copy batched memory pool Fixes: #2898 Signed-off-by: dong sunchao <dongsunchao@gmail.com>
1 parent cff99db commit 0a089bf

File tree

1 file changed

+82
-11
lines changed

1 file changed

+82
-11
lines changed

criu/bfd.c

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
*/
2626
#define BUFSIZE (PAGE_SIZE)
2727

28+
#define BFD_MAX_DYNAMIC_SIZE (2 * 1024 * 1024)
29+
2830
struct bfd_buf {
2931
char *mem;
32+
size_t size;
3033
struct list_head l;
3134
};
3235

@@ -61,6 +64,7 @@ static int buf_get(struct xbuf *xb)
6164
}
6265

6366
b->mem = mem + i * BUFSIZE;
67+
b->size = BUFSIZE;
6468
list_add_tail(&b->l, &bufs);
6569
}
6670
}
@@ -77,11 +81,20 @@ static int buf_get(struct xbuf *xb)
7781

7882
static void buf_put(struct xbuf *xb)
7983
{
80-
/*
81-
* Don't unmap buffer back, it will get reused
82-
* by next bfdopen call
83-
*/
84-
list_add(&xb->buf->l, &bufs);
84+
struct bfd_buf *b = xb->buf;
85+
86+
if (b->size > BUFSIZE) {
87+
/* This buffer was remapped to fit a long line, unmap it back */
88+
munmap(b->mem, b->size);
89+
xfree(b);
90+
} else {
91+
/*
92+
* Don't unmap standard buffer back, it will get reused
93+
* by next bfdopen call
94+
*/
95+
list_add(&b->l, &bufs);
96+
}
97+
8598
xb->buf = NULL;
8699
xb->mem = NULL;
87100
xb->data = NULL;
@@ -140,11 +153,13 @@ static int brefill(struct bfd *f)
140153
{
141154
int ret;
142155
struct xbuf *b = &f->b;
156+
struct bfd_buf *b_buf = b->buf;
157+
size_t cap = b_buf ? b_buf->size : BUFSIZE;
143158

144159
memmove(b->mem, b->data, b->sz);
145160
b->data = b->mem;
146161

147-
ret = read_all(f->fd, b->mem + b->sz, BUFSIZE - b->sz);
162+
ret = read_all(f->fd, b->mem + b->sz, cap - b->sz);
148163
if (ret < 0) {
149164
pr_perror("Error reading file");
150165
return -1;
@@ -175,9 +190,12 @@ char *breadline(struct bfd *f)
175190
char *breadchr(struct bfd *f, char c)
176191
{
177192
struct xbuf *b = &f->b;
193+
struct bfd_buf *b_buf = b->buf;
178194
bool refilled = false;
179195
char *n;
196+
size_t cap;
180197
unsigned int ss = 0;
198+
void *new_mem;
181199

182200
again:
183201
n = strnchr(b->data + ss, b->sz - ss, c);
@@ -195,9 +213,60 @@ char *breadchr(struct bfd *f, char c)
195213
if (!b->sz)
196214
return NULL;
197215

198-
if (b->sz == BUFSIZE) {
199-
pr_err("The bfd buffer is too small\n");
200-
return ERR_PTR(-EIO);
216+
cap = b_buf ? b_buf->size : BUFSIZE;
217+
if (b->sz == cap) {
218+
struct bfd_buf *new_buf;
219+
size_t new_cap = cap * 2;
220+
221+
if (new_cap > BFD_MAX_DYNAMIC_SIZE) {
222+
pr_err("Line too long to fit in BFD_MAX_DYNAMIC_SIZE\n");
223+
return ERR_PTR(-EIO);
224+
}
225+
226+
new_buf = xmalloc(sizeof(*new_buf));
227+
if (!new_buf) {
228+
pr_perror("Unable to allocate buffer for bfd");
229+
return ERR_PTR(-ENOMEM);
230+
}
231+
232+
new_mem = mmap(NULL, new_cap, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
233+
if (new_mem == MAP_FAILED) {
234+
pr_perror("Unable to allocate memory for bfd buffer");
235+
xfree(new_buf);
236+
return ERR_PTR(-ENOMEM);
237+
}
238+
239+
memcpy(new_mem, b->data, b->sz);
240+
241+
if (b_buf) {
242+
if (b_buf->size > BUFSIZE) {
243+
/* This buffer was remapped to fit a long line, unmap it back */
244+
munmap(b_buf->mem, b_buf->size);
245+
xfree(b_buf);
246+
} else {
247+
/*
248+
* Don't unmap standard buffer back, it will get reused
249+
* by next bfdopen call
250+
*/
251+
list_add(&b_buf->l, &bufs);
252+
}
253+
}
254+
b_buf = new_buf;
255+
b->buf = b_buf;
256+
257+
new_buf->mem = new_mem;
258+
new_buf->size = new_cap;
259+
260+
b->mem = new_mem;
261+
b->data = new_mem;
262+
263+
ss = b->sz;
264+
265+
if (brefill(f) < 0)
266+
return ERR_PTR(-EIO);
267+
268+
refilled = true;
269+
goto again;
201270
}
202271
/*
203272
* Last bytes may lack the \n at the
@@ -251,15 +320,17 @@ static int bflush(struct bfd *bfd)
251320
static int __bwrite(struct bfd *bfd, const void *buf, int size)
252321
{
253322
struct xbuf *b = &bfd->b;
323+
struct bfd_buf *b_buf = b->buf;
324+
size_t cap = b_buf ? b_buf->size : BUFSIZE;
254325

255-
if (b->sz + size > BUFSIZE) {
326+
if (b->sz + size > cap) {
256327
int ret;
257328
ret = bflush(bfd);
258329
if (ret < 0)
259330
return ret;
260331
}
261332

262-
if (size > BUFSIZE)
333+
if (size > cap)
263334
return write_all(bfd->fd, buf, size);
264335

265336
memcpy(b->data + b->sz, buf, size);

0 commit comments

Comments
 (0)