contrib/fuzz/fncache.cc
author Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
Sun, 13 Sep 2020 22:14:25 -0400
changeset 46889 8759e22f1649
parent 43870 8766728dbce6
permissions -rw-r--r--
procutil: avoid using os.fork() to implement runbgcommand We ran into the following deadlock: - some command creates an ssh peer, then raises without explicitly closing the peer (hg id + extension in our case) - dispatch catches the exception, calls ui.log('commandfinish', ..) (the sshpeer is still not closed), which calls logtoprocess, which calls procutil.runbgcommand. - in the child of runbgcommand's fork(), between the fork and the exec, the opening of file descriptors triggers a gc which runs the destructor for sshpeer, which waits on ssh's stderr being closed, which never happens since ssh's stderr is held open by the parent of the fork where said destructor hasn't run Remotefilelog appears to have a hack around this deadlock as well. I don't know if there's more subtlety to it, because even though the problem is determistic, it is very fragile, so I didn't manage to reduce it. I can imagine three ways of tackling this problem: 1. don't run any python between fork and exec in runbgcommand 2. make the finalizer harmless after the fork 3. close the peer without relying on gc behavior This commit goes with 1, as forking without exec'ing is tricky in general in a language with gc finalizers. And maybe it's better in the presence of rust threads. A future commit will try 2 or 3. Performance wise: at low memory usage, it's an improvement. At higher memory usage, it's about 2x faster than before when ensurestart=True, but 2x slower when ensurestart=False. Not sure if that matters. The reason for that last bit is that the subprocess.Popen always waits for the execve to finish, and at high memory usage, execve is slow because it deallocates the large page table. Numbers and script: before after mem=1.0GB, ensurestart=True 52.1ms 26.0ms mem=1.0GB, ensurestart=False 14.7ms 26.0ms mem=0.5GB, ensurestart=True 23.2ms 11.2ms mem=0.5GB, ensurestart=False 6.2ms 11.3ms mem=0.2GB, ensurestart=True 15.7ms 7.4ms mem=0.2GB, ensurestart=False 4.3ms 8.1ms mem=0.0GB, ensurestart=True 2.3ms 0.7ms mem=0.0GB, ensurestart=False 0.8ms 0.8ms import time for memsize in [1_000_000_000, 500_000_000, 250_000_000, 0]: mem = 'a' * memsize for ensurestart in [True, False]: now = time.time() n = 100 for i in range(n): procutil.runbgcommand([b'true'], {}, ensurestart=ensurestart) after = time.time() ms = (after - now) / float(n) * 1000 print(f'mem={memsize / 1e9:.1f}GB, ensurestart={ensurestart} -> {ms:.1f}ms') Differential Revision: https://phab.mercurial-scm.org/D9019
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
43152
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     1
#include <Python.h>
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     2
#include <assert.h>
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     3
#include <stdlib.h>
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     4
#include <unistd.h>
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     5
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     6
#include "pyutil.h"
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     7
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     8
#include <iostream>
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
     9
#include <string>
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    10
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    11
extern "C" {
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    12
43870
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    13
static PYCODETYPE *code;
43152
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    14
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    15
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv)
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    16
{
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    17
	contrib::initpy(*argv[0]);
43870
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    18
	code = (PYCODETYPE *)Py_CompileString(R"py(
43152
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    19
try:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    20
    for fn in (
43870
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    21
        parsers.isasciistr,
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    22
        parsers.asciilower,
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    23
        parsers.asciiupper,
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    24
        parsers.encodedir,
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    25
        parsers.pathencode,
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    26
        parsers.lowerencode,
43152
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    27
    ):
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    28
        try:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    29
            fn(data)
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    30
        except UnicodeDecodeError:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    31
            pass  # some functions emit this exception
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    32
        except AttributeError:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    33
            # pathencode needs hashlib, which fails to import because the time
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    34
            # module fails to import. We should try and fix that some day, but
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    35
            # for now we at least get coverage on non-hashencoded codepaths.
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    36
            if fn != pathencode:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    37
                raise
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    38
        # uncomment this for debugging exceptions
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    39
        # except Exception as e:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    40
        #     raise Exception('%r: %r' % (fn, e))
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    41
except Exception as e:
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    42
    pass
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    43
    # uncomment this print if you're editing this Python code
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    44
    # to debug failures.
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    45
    # print(e)
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    46
)py",
43870
8766728dbce6 fuzz: add support for fuzzing under either Python 2 or 3
Augie Fackler <augie@google.com>
parents: 43152
diff changeset
    47
	                                      "fuzzer", Py_file_input);
43152
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    48
	if (!code) {
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    49
		std::cerr << "failed to compile Python code!" << std::endl;
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    50
	}
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    51
	return 0;
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    52
}
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    53
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    54
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    55
{
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    56
	PyObject *mtext =
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    57
	    PyBytes_FromStringAndSize((const char *)Data, (Py_ssize_t)Size);
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    58
	PyObject *locals = PyDict_New();
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    59
	PyDict_SetItemString(locals, "data", mtext);
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    60
	PyObject *res = PyEval_EvalCode(code, contrib::pyglobals(), locals);
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    61
	if (!res) {
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    62
		PyErr_Print();
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    63
	}
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    64
	Py_XDECREF(res);
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    65
	Py_DECREF(locals);
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    66
	Py_DECREF(mtext);
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    67
	return 0; // Non-zero return values are reserved for future use.
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    68
}
b37dd26935ee fuzz: new fuzzer for fncache-related functions
Augie Fackler <augie@google.com>
parents:
diff changeset
    69
}