• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

spesmilo / electrum / 4914436848222208

28 Apr 2025 08:02PM UTC coverage: 60.243% (-0.04%) from 60.284%
4914436848222208

push

CirrusCI

web-flow
Merge pull request #9749 from SomberNight/202504_harden_memory_linux

add harden_memory_linux: harder for other processes to read our memory

0 of 41 new or added lines in 1 file covered. (0.0%)

21625 of 35896 relevant lines covered (60.24%)

3.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/electrum/harden_memory_linux.py
1
# Copyright (C) 2020 cptpcrd
2
# Copyright (C) 2025 The Electrum developers
3
# Distributed under the MIT software license, see the accompanying
4
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
5
#
6
# based on https://github.com/cptpcrd/pyprctl/blob/578ed3e81066a8a61dede912454d5eeaef37eeea/pyprctl/ffi.py#L28
7
#
8
# This module tries to restrict the ability of other processes to access the memory of our process.
9
# Traditionally, on Linux, one process can access the memory of another arbitrary process
10
# if both are running as the same user (uid). (Root can ofc access the memory of ~any process)
11
# Programs can opt-out from this by setting prctl(PR_SET_DUMPABLE, 0);
12
#
13
# Besides PR_SET_DUMPABLE, there are ways to globally restrict this for all processes:
14
# 1. The Yama (Linux Security Module) ptrace scope can be used to reduce these permissions
15
#    This runtime kernel parameter can be set to the following options:
16
#      0 - Default attach security permissions.
17
#      1 - Restricted attach. Only child processes plus normal permissions.
18
#      2 - Admin-only attach. Only executables with CAP_SYS_PTRACE.
19
#      3 - No attach. No process may call ptrace at all. Irrevocable.
20
#    # Note: The default value of kernel.yama.ptrace_scope is distro-specific.
21
#    #       See `$ cat /proc/sys/kernel/yama/ptrace_scope`.
22
#    #       - ubuntu 22.04 sets it to 1 (see /etc/sysctl.d/10-ptrace.conf),
23
#    #       - debian 12 sets it to 0
24
#    #       - manjaro sets it to 1
25
# 2. SELinux: ptrace can be restricted by setting the selinux deny_ptrace boolean.
26
#
27
# For a quick test on your system, try:
28
#   $ cat /proc/$$/mem > /dev/null
29
#   cat: /proc/4907/mem: Permission denied
30
# Getting "Permission denied" means access failed, "Input/output error" means access succeeded.
31

NEW
32
import ctypes
×
NEW
33
import ctypes.util
×
NEW
34
import os
×
NEW
35
import sys
×
NEW
36
from typing import Optional
×
37

NEW
38
from .logging import get_logger
×
39

40

NEW
41
_logger = get_logger(__name__)
×
42

NEW
43
PR_GET_DUMPABLE = 3
×
NEW
44
PR_SET_DUMPABLE = 4
×
45

46

NEW
47
_libc = None  # type: Optional[ctypes.CDLL]
×
NEW
48
def _load_libc():
×
49
    global _libc
NEW
50
    if _libc is not None:
×
NEW
51
        return
×
52
    #assert sys.platform == "linux", sys.platform
53
    # note: find_library can raise FileNotFoundError(OSError), see https://github.com/python/cpython/issues/93094
NEW
54
    _libc_path = ctypes.util.find_library("c")
×
NEW
55
    _libc = ctypes.CDLL(_libc_path, use_errno=True)
×
NEW
56
    _libc.prctl.argtypes = (ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong)
×
NEW
57
    _libc.prctl.restype = ctypes.c_int
×
58

59

NEW
60
def set_dumpable(flag: bool) -> None:
×
61
    """Set the "dumpable" attribute on the current process.
62
    This controls whether a core dump will be produced if the process receives a signal whose
63
    default behavior is to produce a core dump.
64
    In addition, processes that are not dumpable cannot be attached with ptrace() PTRACE_ATTACH.
65

66
    In effect, another process running as the same user as us can read our memory if we are dumpable.
67
    """
NEW
68
    _load_libc()
×
NEW
69
    res = _libc.prctl(PR_SET_DUMPABLE, int(bool(flag)), 0, 0, 0)
×
NEW
70
    if res < 0:
×
NEW
71
        eno = ctypes.get_errno()
×
NEW
72
        raise OSError(eno, os.strerror(eno), None, None, None)
×
73

74

NEW
75
def set_dumpable_safe(flag: bool) -> None:
×
NEW
76
    try:
×
NEW
77
        _load_libc()
×
NEW
78
    except Exception as e:
×
NEW
79
        _logger.exception("error loading libc")
×
NEW
80
        return
×
NEW
81
    assert _libc is not None
×
NEW
82
    try:
×
NEW
83
        set_dumpable(flag)
×
NEW
84
    except OSError as e:
×
NEW
85
        _logger.error(f"libc.prctl(PR_SET_DUMPABLE, {flag}) errored: {e}")
×
86

87

NEW
88
def get_dumpable() -> bool:
×
NEW
89
    _load_libc()
×
NEW
90
    res = _libc.prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)
×
NEW
91
    if res < 0:
×
NEW
92
        eno = ctypes.get_errno()
×
NEW
93
        raise OSError(eno, os.strerror(eno), None, None, None)
×
NEW
94
    return res != 0
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc