0001
0002
0003
0004
0005
0006 #include <unistd.h>
0007 #include <sys/resource.h>
0008 #include <sys/capability.h>
0009 #include <sys/mman.h>
0010 #include <fcntl.h>
0011 #include <string.h>
0012 #include <sys/ipc.h>
0013 #include <sys/shm.h>
0014 #include <time.h>
0015 #include "mlock2.h"
0016
0017 #define CHUNK_UNIT (128 * 1024)
0018 #define MLOCK_RLIMIT_SIZE (CHUNK_UNIT * 2)
0019 #define MLOCK_WITHIN_LIMIT_SIZE CHUNK_UNIT
0020 #define MLOCK_OUTOF_LIMIT_SIZE (CHUNK_UNIT * 3)
0021
0022 #define TEST_LOOP 100
0023 #define PAGE_ALIGN(size, ps) (((size) + ((ps) - 1)) & ~((ps) - 1))
0024
0025 int set_cap_limits(rlim_t max)
0026 {
0027 struct rlimit new;
0028 cap_t cap = cap_init();
0029
0030 new.rlim_cur = max;
0031 new.rlim_max = max;
0032 if (setrlimit(RLIMIT_MEMLOCK, &new)) {
0033 perror("setrlimit() returns error\n");
0034 return -1;
0035 }
0036
0037
0038 if (cap_set_proc(cap)) {
0039 perror("cap_set_proc() returns error\n");
0040 return -2;
0041 }
0042
0043 return 0;
0044 }
0045
0046 int get_proc_locked_vm_size(void)
0047 {
0048 FILE *f;
0049 int ret = -1;
0050 char line[1024] = {0};
0051 unsigned long lock_size = 0;
0052
0053 f = fopen("/proc/self/status", "r");
0054 if (!f) {
0055 perror("fopen");
0056 return -1;
0057 }
0058
0059 while (fgets(line, 1024, f)) {
0060 if (strstr(line, "VmLck")) {
0061 ret = sscanf(line, "VmLck:\t%8lu kB", &lock_size);
0062 if (ret <= 0) {
0063 printf("sscanf() on VmLck error: %s: %d\n",
0064 line, ret);
0065 fclose(f);
0066 return -1;
0067 }
0068 fclose(f);
0069 return (int)(lock_size << 10);
0070 }
0071 }
0072
0073 perror("cannot parse VmLck in /proc/self/status\n");
0074 fclose(f);
0075 return -1;
0076 }
0077
0078
0079
0080
0081
0082
0083
0084
0085 int get_proc_page_size(unsigned long addr)
0086 {
0087 FILE *smaps;
0088 char *line;
0089 unsigned long mmupage_size = 0;
0090 size_t size;
0091
0092 smaps = seek_to_smaps_entry(addr);
0093 if (!smaps) {
0094 printf("Unable to parse /proc/self/smaps\n");
0095 return 0;
0096 }
0097
0098 while (getline(&line, &size, smaps) > 0) {
0099 if (!strstr(line, "MMUPageSize")) {
0100 free(line);
0101 line = NULL;
0102 size = 0;
0103 continue;
0104 }
0105
0106
0107 if (sscanf(line, "MMUPageSize: %8lu kB",
0108 &mmupage_size) < 1) {
0109 printf("Unable to parse smaps entry for Size:%s\n",
0110 line);
0111 break;
0112 }
0113
0114 }
0115 free(line);
0116 if (smaps)
0117 fclose(smaps);
0118 return mmupage_size << 10;
0119 }
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138 int test_mlock_within_limit(char *p, int alloc_size)
0139 {
0140 int i;
0141 int ret = 0;
0142 int locked_vm_size = 0;
0143 struct rlimit cur;
0144 int page_size = 0;
0145
0146 getrlimit(RLIMIT_MEMLOCK, &cur);
0147 if (cur.rlim_cur < alloc_size) {
0148 printf("alloc_size[%d] < %u rlimit,lead to mlock failure\n",
0149 alloc_size, (unsigned int)cur.rlim_cur);
0150 return -1;
0151 }
0152
0153 srand(time(NULL));
0154 for (i = 0; i < TEST_LOOP; i++) {
0155
0156
0157
0158
0159
0160
0161 int is_mlock = !!(rand() % 2);
0162 int lock_size = rand() % alloc_size;
0163 int start_offset = rand() % (alloc_size - lock_size);
0164
0165 if (is_mlock)
0166 ret = mlock(p + start_offset, lock_size);
0167 else
0168 ret = mlock2_(p + start_offset, lock_size,
0169 MLOCK_ONFAULT);
0170
0171 if (ret) {
0172 printf("%s() failure at |%p(%d)| mlock:|%p(%d)|\n",
0173 is_mlock ? "mlock" : "mlock2",
0174 p, alloc_size,
0175 p + start_offset, lock_size);
0176 return ret;
0177 }
0178 }
0179
0180
0181
0182
0183 locked_vm_size = get_proc_locked_vm_size();
0184 page_size = get_proc_page_size((unsigned long)p);
0185 if (page_size == 0) {
0186 printf("cannot get proc MMUPageSize\n");
0187 return -1;
0188 }
0189
0190 if (locked_vm_size > PAGE_ALIGN(alloc_size, page_size) + page_size) {
0191 printf("test_mlock_within_limit() left VmLck:%d on %d chunk\n",
0192 locked_vm_size, alloc_size);
0193 return -1;
0194 }
0195
0196 return 0;
0197 }
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215 int test_mlock_outof_limit(char *p, int alloc_size)
0216 {
0217 int i;
0218 int ret = 0;
0219 int locked_vm_size = 0, old_locked_vm_size = 0;
0220 struct rlimit cur;
0221
0222 getrlimit(RLIMIT_MEMLOCK, &cur);
0223 if (cur.rlim_cur >= alloc_size) {
0224 printf("alloc_size[%d] >%u rlimit, violates test condition\n",
0225 alloc_size, (unsigned int)cur.rlim_cur);
0226 return -1;
0227 }
0228
0229 old_locked_vm_size = get_proc_locked_vm_size();
0230 srand(time(NULL));
0231 for (i = 0; i < TEST_LOOP; i++) {
0232 int is_mlock = !!(rand() % 2);
0233 int lock_size = (rand() % (alloc_size - cur.rlim_cur))
0234 + cur.rlim_cur;
0235 int start_offset = rand() % (alloc_size - lock_size);
0236
0237 if (is_mlock)
0238 ret = mlock(p + start_offset, lock_size);
0239 else
0240 ret = mlock2_(p + start_offset, lock_size,
0241 MLOCK_ONFAULT);
0242 if (ret == 0) {
0243 printf("%s() succeeds? on %p(%d) mlock%p(%d)\n",
0244 is_mlock ? "mlock" : "mlock2",
0245 p, alloc_size,
0246 p + start_offset, lock_size);
0247 return -1;
0248 }
0249 }
0250
0251 locked_vm_size = get_proc_locked_vm_size();
0252 if (locked_vm_size != old_locked_vm_size) {
0253 printf("tests leads to new mlocked page: old[%d], new[%d]\n",
0254 old_locked_vm_size,
0255 locked_vm_size);
0256 return -1;
0257 }
0258
0259 return 0;
0260 }
0261
0262 int main(int argc, char **argv)
0263 {
0264 char *p = NULL;
0265 int ret = 0;
0266
0267 if (set_cap_limits(MLOCK_RLIMIT_SIZE))
0268 return -1;
0269
0270 p = malloc(MLOCK_WITHIN_LIMIT_SIZE);
0271 if (p == NULL) {
0272 perror("malloc() failure\n");
0273 return -1;
0274 }
0275 ret = test_mlock_within_limit(p, MLOCK_WITHIN_LIMIT_SIZE);
0276 if (ret)
0277 return ret;
0278 munlock(p, MLOCK_WITHIN_LIMIT_SIZE);
0279 free(p);
0280
0281
0282 p = malloc(MLOCK_OUTOF_LIMIT_SIZE);
0283 if (p == NULL) {
0284 perror("malloc() failure\n");
0285 return -1;
0286 }
0287 ret = test_mlock_outof_limit(p, MLOCK_OUTOF_LIMIT_SIZE);
0288 if (ret)
0289 return ret;
0290 munlock(p, MLOCK_OUTOF_LIMIT_SIZE);
0291 free(p);
0292
0293 return 0;
0294 }