Coverage Report

Created: 2025-09-05 07:01

/src/tor/src/lib/meminfo/meminfo.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2003-2004, Roger Dingledine
2
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3
 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4
/* See LICENSE for licensing information */
5
6
/**
7
 * \file meminfo.c
8
 *
9
 * \brief Functions to query total memory, and access meta-information about
10
 * the allocator.
11
 **/
12
13
#include "lib/meminfo/meminfo.h"
14
15
#include "lib/cc/compat_compiler.h"
16
#include "lib/cc/torint.h"
17
#include "lib/fs/files.h"
18
#include "lib/log/log.h"
19
#include "lib/malloc/malloc.h"
20
#include "lib/string/util_string.h"
21
22
#ifdef HAVE_FCNTL_H
23
#include <fcntl.h>
24
#endif
25
#ifdef HAVE_MALLOC_H
26
#include <malloc.h>
27
#endif
28
#ifdef HAVE_UNISTD_H
29
#include <unistd.h>
30
#endif
31
32
#ifdef _WIN32
33
#include <windows.h>
34
#endif
35
#include <string.h>
36
37
#if defined(HAVE_SYS_SYSCTL_H) && !defined(_WIN32) && !defined(__linux__)
38
#include <sys/sysctl.h>
39
#endif
40
41
#if defined(HW_PHYSMEM64)
42
/* OpenBSD and NetBSD define this */
43
#define INT64_HW_MEM HW_PHYSMEM64
44
#elif defined(HW_MEMSIZE)
45
/* OSX defines this one */
46
#define INT64_HW_MEM HW_MEMSIZE
47
#endif /* defined(HW_PHYSMEM64) || ... */
48
49
/**
50
 * Helper: try to detect the total system memory, and return it. On failure,
51
 * return 0.
52
 */
53
static uint64_t
54
get_total_system_memory_impl(void)
55
0
{
56
0
#if defined(__linux__)
57
  /* On linux, sysctl is deprecated. Because proc is so awesome that you
58
   * shouldn't _want_ to write portable code, I guess? */
59
0
  unsigned long long result=0;
60
0
  int fd = -1;
61
0
  char *s = NULL;
62
0
  const char *cp;
63
0
  size_t file_size=0;
64
0
  if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0)))
65
0
    return 0;
66
0
  s = read_file_to_str_until_eof(fd, 65536, &file_size);
67
0
  if (!s)
68
0
    goto err;
69
0
  cp = find_str_at_start_of_line(s, "MemTotal:");
70
0
  if (!cp)
71
0
    goto err;
72
  /* Use the system sscanf so that space will match a wider number of space */
73
0
  if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1)
74
0
    goto err;
75
76
0
  close(fd);
77
0
  tor_free(s);
78
0
  return result * 1024;
79
80
  /* LCOV_EXCL_START Can't reach this unless proc is broken. */
81
0
 err:
82
0
  tor_free(s);
83
0
  close(fd);
84
0
  return 0;
85
  /* LCOV_EXCL_STOP */
86
#elif defined (_WIN32)
87
  /* Windows has MEMORYSTATUSEX; pretty straightforward. */
88
  MEMORYSTATUSEX ms;
89
  memset(&ms, 0, sizeof(ms));
90
  ms.dwLength = sizeof(ms);
91
  if (! GlobalMemoryStatusEx(&ms))
92
    return 0;
93
94
  return ms.ullTotalPhys;
95
96
#elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM)
97
  /* On many systems, HW_PHYSMEM is clipped to 32 bits; let's use a better
98
   * variant if we know about it. */
99
  uint64_t memsize = 0;
100
  size_t len = sizeof(memsize);
101
  int mib[2] = {CTL_HW, INT64_HW_MEM};
102
  if (sysctl(mib,2,&memsize,&len,NULL,0))
103
    return 0;
104
105
  return memsize;
106
107
#elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM)
108
  /* On some systems (like FreeBSD I hope) you can use a size_t with
109
   * HW_PHYSMEM. */
110
  size_t memsize=0;
111
  size_t len = sizeof(memsize);
112
  int mib[2] = {CTL_HW, HW_PHYSMEM};
113
  if (sysctl(mib,2,&memsize,&len,NULL,0))
114
    return 0;
115
116
  return memsize;
117
118
#else
119
  /* I have no clue. */
120
  return 0;
121
#endif /* defined(__linux__) || ... */
122
0
}
123
124
/**
125
 * Try to find out how much physical memory the system has. On success,
126
 * return 0 and set *<b>mem_out</b> to that value. On failure, return -1.
127
 */
128
MOCK_IMPL(int,
129
get_total_system_memory, (size_t *mem_out))
130
0
{
131
0
  static size_t mem_cached=0;
132
0
  uint64_t m = get_total_system_memory_impl();
133
0
  if (0 == m) {
134
    /* LCOV_EXCL_START -- can't make this happen without mocking. */
135
    /* We couldn't find our memory total */
136
0
    if (0 == mem_cached) {
137
      /* We have no cached value either */
138
0
      *mem_out = 0;
139
0
      return -1;
140
0
    }
141
142
0
    *mem_out = mem_cached;
143
0
    return 0;
144
    /* LCOV_EXCL_STOP */
145
0
  }
146
147
#if SIZE_MAX != UINT64_MAX
148
  if (m > SIZE_MAX) {
149
    /* I think this could happen if we're a 32-bit Tor running on a 64-bit
150
     * system: we could have more system memory than would fit in a
151
     * size_t. */
152
    m = SIZE_MAX;
153
  }
154
#endif /* SIZE_MAX != UINT64_MAX */
155
156
0
  *mem_out = mem_cached = (size_t) m;
157
158
0
  return 0;
159
0
}