1 /*
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <gtest/gtest.h>
17 #include <fcntl.h>
18 #include <cinttypes>
19 #include <climits>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <unistd.h>
23 #include <iomanip>
24 #include <iostream>
25 #include <string>
26 #include <vector>
27 #include <sys/mman.h>
28 #include <sys/utsname.h>
29 #include <string>
30 #include <memory.h>
31 #include "securec.h"
32 
33 #define PAGE_SIZE 4096
34 #define NR_HUGEPAGES 10U
35 #define NR_OVERCOMMIT_HUGEPAGES 20U
36 #define HUGETLB_SHIFT 21
37 #define OVERCOMMIT_RATIO 1000000000
38 #define KB_SHIFT 10U
39 #define NUM 1024
40 using namespace testing::ext;
41 using namespace std;
42 
43 class MmapVApiTest : public testing::Test {
44 public:
45     static void SetUpTestCase();
46     static void TearDownTestCase();
47     void SetUp();
48     void TearDown();
49 private:
50 };
SetUp()51 void MmapVApiTest::SetUp()
52 {
53 }
TearDown()54 void MmapVApiTest::TearDown()
55 {
56 }
SetUpTestCase()57 void MmapVApiTest::SetUpTestCase()
58 {
59 }
TearDownTestCase()60 void MmapVApiTest::TearDownTestCase()
61 {
62 }
63 
64 
65 enum {
66     OVERCOMMIT_GUESS,
67     OVERCOMMIT_NEVER,
68 };
69 
70 static int g_overcommitPolicy;
71 static int g_overcommitRatio;
72 
SetProc(string entry, int input)73 static void SetProc(string entry, int input)
74 {
75     FILE *fp = fopen(entry.c_str(), "w");
76     if (fp != nullptr) {
77         (void)fprintf(fp, "%d", input);
78         (void)fclose(fp);
79     }
80 }
81 
FetchProc(string entry, int *output)82 static void FetchProc(string entry, int *output)
83 {
84     char buffer[NUM];
85     FILE *fp = fopen(entry.c_str(), "r");
86     if (fp != nullptr) {
87         int ret = snprintf_s(buffer, sizeof(buffer),  sizeof(buffer) - 1, "%d", *output);
88         if (ret == 0) {
89             if (fputs(buffer, fp) == EOF) {
90                 std::cout << "Error fputs .\n" << std::endl;
91             }
92         }
93     }
94 }
95 
SetOvercommitMemory(int input)96 static void SetOvercommitMemory(int input)
97 {
98     SetProc("/proc/sys/vm/overcommit_memory", input);
99 }
100 
FetchOvercommitMemory(int *output)101 static void FetchOvercommitMemory(int *output)
102 {
103     FetchProc("/proc/sys/vm/overcommit_memory", output);
104 }
105 
SetOvercommitRatio(int input)106 static void SetOvercommitRatio(int input)
107 {
108     SetProc("/proc/sys/vm/overcommit_ratio", input);
109 }
110 
FetchOvercommitRatio(int *output)111 static void FetchOvercommitRatio(int *output)
112 {
113     FetchProc("/proc/sys/vm/overcommit_ratio", output);
114 }
115 
TstSmaps(void *va)116 static int TstSmaps(void *va)
117 {
118     FILE *fp;
119     char cmd[NUM];
120     char line[NUM];
121     char *p;
122     int rss = 0;
123     unsigned long ptr = reinterpret_cast<unsigned long>(va);
124     int ret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
125          "cat /proc/%d/smaps |grep -A 10 '%lx-' |grep 'Rss:'", getpid(), ptr);
126     if (ret < 0) {
127         std::cout << "Error snprintf_s open .\n" << std::endl;
128         return ret;
129     }
130     fp = popen(cmd, "r");
131     if (fp == nullptr) {
132         std::cout << "Error popen open .\n" << std::endl;
133         return -1;
134     }
135     while (fgets(line, NUM, fp) != nullptr) {
136         p = strstr(line, "Rss:");
137         if (p != nullptr) {
138             int err = sscanf_s(p, "Rss: %d KB", &rss);
139             if (err < 0) {
140                 std::cout << "Error sscanf_s open .\n" << std::endl;
141                 return err;
142             }
143             break;
144         }
145     }
146     pclose(fp);
147     return rss;
148 }
149 
ForceReclaim(void)150 static void ForceReclaim(void)
151 {
152     FILE *fp = fopen("/proc/self/reclaim", "w");
153     if (fp != nullptr) {
154         fputs("5", fp);
155         fclose(fp);
156     } else {
157         std::cout << "Error ForceReclaim open .\n" << std::endl;
158         ASSERT_TRUE(fp != nullptr);
159     }
160 }
161 
162 /*
163  * @tc.number SUB_KERNEL_MEM_MAP_0600
164  * @tc.name   MMAPNoreServe001
165  * @tc.desc   mmap use MAP_NORESERVE without OVERCOMMIT_NEVER
166 */
HWTEST_F(MmapVApiTest, MMAPNoreServe001, Function | MediumTest | Level1)167 HWTEST_F(MmapVApiTest, MMAPNoreServe001, Function | MediumTest | Level1)
168 {
169     FetchOvercommitMemory(&g_overcommitPolicy);
170     ASSERT_TRUE(g_overcommitPolicy >= 0);
171     FetchOvercommitRatio(&g_overcommitRatio);
172     ASSERT_TRUE(g_overcommitRatio >= 0);
173     SetOvercommitRatio(OVERCOMMIT_RATIO);
174     SetOvercommitMemory(OVERCOMMIT_GUESS);
175     void *va = mmap(nullptr, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT), PROT_READ | PROT_WRITE,
176          MAP_ANON | MAP_PRIVATE | MAP_HUGETLB | MAP_NORESERVE, -1, 0);
177     ASSERT_TRUE(va != MAP_FAILED);
178     int ret = munmap(va, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT));
179     ASSERT_TRUE(ret == 0);
180     va = nullptr;
181 }
182 
183 /*
184  * @tc.number SUB_KERNEL_MEM_MAP_0700
185  * @tc.name   MMAPNoreServe002
186  * @tc.desc   mmap use MAP_NORESERVE with OVERCOMMIT_NEVER
187 */
HWTEST_F(MmapVApiTest, MMAPNoreServe002, Function | MediumTest | Level1)188 HWTEST_F(MmapVApiTest, MMAPNoreServe002, Function | MediumTest | Level1)
189 {
190     FetchOvercommitMemory(&g_overcommitPolicy);
191     ASSERT_TRUE(g_overcommitPolicy >= 0);
192     FetchOvercommitRatio(&g_overcommitRatio);
193     ASSERT_TRUE(g_overcommitRatio >= 0);
194     SetOvercommitRatio(OVERCOMMIT_RATIO);
195     SetOvercommitMemory(OVERCOMMIT_NEVER);
196 
197     void *va = mmap(nullptr, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT), PROT_READ | PROT_WRITE,
198          MAP_ANON | MAP_PRIVATE | MAP_HUGETLB | MAP_NORESERVE, -1, 0);
199     ASSERT_TRUE(va != MAP_FAILED);
200     int ret = munmap(va, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT));
201     ASSERT_TRUE(ret == 0);
202     va = nullptr;
203 }
204 
205 
206 /*
207  * @tc.number SUB_KERNEL_MEM_MAP_1100
208  * @tc.name   MMAPNonBlock002
209  * @tc.desc   mmap use MAP_NONBLOCK with MAP_POPULATE
210 */
HWTEST_F(MmapVApiTest, MMAPNonBlock002, Function | MediumTest | Level1)211 HWTEST_F(MmapVApiTest, MMAPNonBlock002, Function | MediumTest | Level1)
212 {
213     void *va = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE,
214          MAP_ANON | MAP_PRIVATE | MAP_NONBLOCK | MAP_POPULATE, -1, 0);
215     ASSERT_TRUE(va != MAP_FAILED);
216     int ret = munmap(va, PAGE_SIZE);
217     ASSERT_TRUE(ret == 0);
218     va = nullptr;
219 }
220 
221 /*
222  * @tc.number SUB_KERNEL_MEM_MAP_1200
223  * @tc.name   mlockall001
224  * @tc.desc   mlockall current pthread
225 */
HWTEST_F(MmapVApiTest, mlockall001, Function | MediumTest | Level1)226 HWTEST_F(MmapVApiTest, mlockall001, Function | MediumTest | Level1)
227 {
228     void *va = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
229     ASSERT_TRUE(va != MAP_FAILED);
230     int rc = mlockall(MCL_CURRENT | MCL_ONFAULT);
231     ASSERT_TRUE(rc == 0);
232     int64_t rss = TstSmaps(va);
233     ASSERT_TRUE(rss == 0LL);
234     *(char *)va = 1;
235     ForceReclaim();
236     rss = TstSmaps(va);
237     ASSERT_TRUE(rss == (PAGE_SIZE >> KB_SHIFT));
238     rc = munlockall();
239     ASSERT_TRUE(rc == 0);
240     int ret = munmap(va, PAGE_SIZE);
241     ASSERT_TRUE(ret == 0);
242     va = nullptr;
243 }
244 
245 /*
246  * @tc.number SUB_KERNEL_MEM_MAP_1300
247  * @tc.name   mlockall002
248  * @tc.desc   create vregion after mlockall
249 */
HWTEST_F(MmapVApiTest, mlockall002, Function | MediumTest | Level1)250 HWTEST_F(MmapVApiTest, mlockall002, Function | MediumTest | Level1)
251 {
252     int rc = mlockall(MCL_CURRENT | MCL_ONFAULT);
253     ASSERT_TRUE(rc == 0);
254     void *va = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
255     ASSERT_TRUE(va != MAP_FAILED);
256     int64_t rss = TstSmaps(va);
257     ASSERT_TRUE(rss == 0LL);
258     *(char *)va = 1;
259     ForceReclaim();
260     rss = TstSmaps(va);
261     ASSERT_TRUE(rss == (PAGE_SIZE >> KB_SHIFT));
262     rc = munlockall();
263     ASSERT_TRUE(rc == 0);
264     int ret = munmap(va, PAGE_SIZE);
265     ASSERT_TRUE(ret == 0);
266     va = nullptr;
267 }