Coverage Report

Created: 2025-08-03 06:43

/src/tarantool/src/lua/alloc.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: BSD-2-Clause
3
 *
4
 * Copyright 2010-2024, Tarantool AUTHORS, please see AUTHORS file.
5
 */
6
#include "lua/utils.h"
7
8
#include <lauxlib.h> /* luaL_error */
9
10
#define LUA_MEMORY_LIMIT_DEFAULT (2ULL * 1024 * 1024 * 1024)
11
12
/**
13
 * Default allocator function which is wrapped into a new one
14
 * with the Lua memory limit checker.
15
 */
16
static lua_Alloc orig_alloc;
17
/**
18
 * Memory limit for LuaJIT.
19
 */
20
static size_t memory_limit = LUA_MEMORY_LIMIT_DEFAULT;
21
/**
22
 * Amount of memory used by LuaJIT.
23
 */
24
static size_t used;
25
26
/**
27
 * Lua custom memory allocation function. It extends the original
28
 * one with a memory counter and a limit check.
29
 */
30
static void *
31
alloc_with_limit(void *ud, void *ptr, size_t osize, size_t nsize)
32
0
{
33
0
  size_t new_used = used + nsize - osize;
34
0
  if (new_used > memory_limit) {
35
    /*
36
     * Returning NULL results in the "not enough
37
     * memory" error.
38
     */
39
0
    return NULL;
40
0
  }
41
42
0
  void *result = orig_alloc(ud, ptr, osize, nsize);
43
44
0
  if (result != NULL || nsize == 0)
45
0
    used = new_used;
46
47
  /*
48
   * Result may be NULL, in this case "not enough memory"
49
   * is raised.
50
   */
51
0
  return result;
52
0
}
53
54
static int
55
set_alloc_limit(size_t new_memory_limit)
56
0
{
57
0
  if (new_memory_limit < used) {
58
0
    diag_set(LuajitError, "Cannot limit the Lua memory with values "
59
0
       "less than the currently allocated amount");
60
0
    return -1;
61
0
  }
62
0
  memory_limit = new_memory_limit;
63
0
  return 0;
64
0
}
65
66
void
67
luaT_initalloc(struct lua_State *L)
68
0
{
69
0
  assert(L != NULL);
70
71
0
  used = luaL_getgctotal(L);
72
73
0
  void *orig_ud;
74
0
  orig_alloc = lua_getallocf(L, &orig_ud);
75
76
0
  assert(orig_alloc != NULL);
77
78
0
  lua_setallocf(L, alloc_with_limit, orig_ud);
79
0
}
80
81
/**
82
 * alloc.getlimit() -- get the allocator memory limit.
83
 */
84
static int
85
lbox_alloc_getlimit(struct lua_State *L)
86
0
{
87
0
  lua_pushinteger(L, memory_limit);
88
0
  return 1;
89
0
}
90
91
/**
92
 * alloc.setlimit() -- set the allocator memory limit.
93
 * Returns old memory limit.
94
 */
95
static int
96
lbox_alloc_setlimit(struct lua_State *L)
97
0
{
98
0
  if (lua_gettop(L) < 1) {
99
0
    diag_set(IllegalParams, "Usage: alloc.setlimit(amount)");
100
0
    return luaT_error(L);
101
0
  }
102
103
0
  ssize_t amount = luaL_checkinteger(L, 1);
104
105
0
  if (amount < 0) {
106
0
    diag_set(IllegalParams, "Invalid memory limit: "
107
0
       "the value must be >= 0");
108
0
    return luaT_error(L);
109
0
  }
110
111
0
  size_t old_memory_limit = memory_limit;
112
113
0
  if (set_alloc_limit(amount) < 0)
114
0
    return luaT_error(L);
115
116
0
  lua_pushinteger(L, old_memory_limit);
117
0
  return 1;
118
0
}
119
120
/**
121
 * alloc.used() -- get the amount of the allocated memory.
122
 */
123
static int
124
lbox_alloc_used(struct lua_State *L)
125
0
{
126
0
  lua_pushinteger(L, used);
127
0
  return 1;
128
0
}
129
130
/**
131
 * alloc.unused() -- get the amount of the unused memory.
132
 */
133
static int
134
lbox_alloc_unused(struct lua_State *L)
135
0
{
136
0
  lua_pushinteger(L, memory_limit - used);
137
0
  return 1;
138
0
}
139
140
void
141
tarantool_lua_alloc_init(struct lua_State *L)
142
0
{
143
0
  static const struct luaL_Reg alloc_methods[] = {
144
0
    {"setlimit", lbox_alloc_setlimit},
145
0
    {"getlimit", lbox_alloc_getlimit},
146
0
    {"used",     lbox_alloc_used},
147
0
    {"unused",   lbox_alloc_unused},
148
0
    {NULL, NULL}
149
0
  };
150
151
0
  luaT_newmodule(L, "internal.alloc", alloc_methods);
152
0
  lua_pop(L, 1);
153
0
}