mybatis 换 mybatis-plus 后启动异常,sqlSessionFactory OutOfMemoryError: GC overhead limit exceeded

MyBatis换MyBatis-Plus后启动异常问题
将老项目中的mybatis替换为mybatis-plus后,虽然代码生成器正常工作,但在执行单元测试时遇到启动异常。原本的pom配置下项目可以顺利运行,但更新后频繁出现OutOfMemoryError: GC overhead limit exceeded。尝试按照mybatis-plus官网的快速启动教程配置,甚至在新项目中按步骤操作,均未能解决问题。最终,通过下载并运行mybatis-plus的官方示例项目(基于h2数据库),并在修改为oracle数据库后成功运行。然而,将此示例项目的配置与自己的项目逐一比对,未能找出导致自己项目启动失败的具体原因。

老项目使用 

    <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.5</version>
     </dependency>

替换为 

	<dependency>
	    <groupId>com.baomidou</groupId>
	    <artifactId>mybatis-plus</artifactId>
	    <version>3.4.1</version>
	</dependency>  

已经可以正常使用代码生成器,但是运行之前的单元测试,发现出现异常,

15:15:43.064 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class cn.com.git.batch.service.CheckConfigServiceTest]
15:15:43.076 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
15:15:43.109 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
15:15:43.283 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [cn.com.git.batch.service.CheckConfigServiceTest] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
15:15:43.359 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [cn.com.git.batch.service.CheckConfigServiceTest], using SpringBootContextLoader
15:15:43.378 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [cn.com.git.batch.service.CheckConfigServiceTest]: class path resource [cn/com/git/batch/service/CheckConfigServiceTest-context.xml] does not exist
15:15:43.380 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [cn.com.git.batch.service.CheckConfigServiceTest]: class path resource [cn/com/git/batch/service/CheckConfigServiceTestContext.groovy] does not exist
15:15:43.381 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [cn.com.git.batch.service.CheckConfigServiceTest]: no resource found for suffixes {-context.xml, Context.groovy}.
15:15:43.384 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [cn.com.git.batch.service.CheckConfigServiceTest]: CheckConfigServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
15:15:43.571 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [cn.com.git.batch.service.CheckConfigServiceTest]
15:15:44.135 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [C:\Users\git\git\batch-springboot\batch-springboot\batch-view\target\classes\cn\com\git\WebApplication.class]
15:15:44.142 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration cn.com.git.WebApplication for test class cn.com.git.batch.service.CheckConfigServiceTest
15:15:44.714 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [cn.com.git.batch.service.CheckConfigServiceTest]: using defaults.
15:15:44.717 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
15:15:44.793 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@59af0466, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3e6ef8ad, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@346d61be, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@55536d9e, org.springframework.test.context.support.DirtiesContextTestExecutionListener@747edf66, org.springframework.test.context.transaction.TransactionalTestExecutionListener@3d1cfad4, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@62230c58, org.springframework.test.context.event.EventPublishingTestExecutionListener@2cd2a21f, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@2e55dd0c, org.springframework.boot.t
这样编译可以通过吗,需要改进什么吗 char gCookieAcsPath[MAX_PATH_LEN]={0}; #define MAX_DOMAIN_LEN (64) char gCookieAcsDomain[MAX_DOMAIN_LEN]={0}; static const char *public_suffixes[] = { "com", "net", "org", "edu", "gov", "mil", "int", "arpa", "uk", "us", "jp", "fr", "co.uk", "org.uk", "gov.uk", "ac.uk", "pvt.k12.wy.us" }; UBOOL8 is_public_suffix(const char* domain) { for (int i = 0; public_suffixes[i] != NULL; i++) { if (CUTIL_SAME_STR_NO_CASE(public_suffixes[i], domain)) { return true; } } return false; } char gCookieAcsPath[MAX_PATH_LEN]={0}; #define MAX_DOMAIN_LEN (64) char gCookieAcsDomain[MAX_DOMAIN_LEN]={0}; statuc int getcookie() { //省略 UBOOL8 cookieValid = TRUE; char domainBuf[MAX_DOMAIN_LEN] = {0}; const char *pdomain_without_dot = NULL; // 初始化为NULL //解析报文,省略 else if (strncasecmp(pCookieTmp, "Domain", 6) == 0) { pCookieTmp +=7; /* if (pCookie->domain[0] == 0) { strncpy(pCookie->domain, pCookieTmp, p - pCookieTmp); } */ // Verify the Domain CUTIL_STR_SSTRNCPY(domainBuf, p - pCookieTmp, pCookieTmp); domainBuf[p - pCookieTmp] = '\0'; if (is_public_suffix(domainBuf)) { cookieValid = FALSE; CWMP_ERROR("Rejected public suffix: %s\n", domainBuf); } else if (CUTIL_EMPTY_STR(domainBuf) || CUTIL_SAME_STR_NO_CASE(domainBuf, gCookieAcsDomain)) { /*Accept empty domain names and directly matching domain names.*/ CUTIL_STR_SSTRNCPY(pCookie->domain, p - pCookieTmp, domainBuf); CWMP_DEBUG("Accepted valid domain: %s\n", pCookie->domain); } else if ('.' == domainBuf[0]) { pDomain_without_dot = domainBuf + 1; if (CUTIL_SAME_STR_NO_CASE(pDomain_without_dot, gCookieAcsDomain)) { CUTIL_STR_SSTRNCPY(pCookie->domain, p - pCookieTmp, domainBuf); } else { cookieValid = FALSE; CWMP_ERROR("Domain not allowed: %s (expected: %s)\n", domainBuf, gCookieAcsDomain); } } else { /* Reject all other situations.*/ cookieValid = FALSE; CWMP_ERROR("Domain not allowed: %s (expected: %s)\n", domainBuf, gCookieAcsDomain); } } }
最新发布
08-27
<think>我们注意到代码中有几个问题需要改进: 1. 在`public_suffixes`数组的末尾没有NULL指针,这会导致循环越界访问。因为`is_public_suffix`函数中循环条件是`public_suffixes[i] != NULL`,但数组中没有NULL结尾。 2. 在`getcookie`函数中,变量`pdomain_without_dot`(在代码中后面使用为`pDomain_without_dot`)的声明和赋值存在拼写不一致(声明为`pdomain_without_dot`,但后面使用为`pDomain_without_dot`)。C语言是大小写敏感的,这会导致编译错误。 3. 在`else if ('.' == domainBuf[0])`分支中,使用了未声明的变量`pDomain_without_dot`(注意大小写)。实际上,前面声明的是`pdomain_without_dot`(小写p开头),而后面使用是大写P开头。这会导致编译错误。 4. 在`is_public_suffix`函数中,我们只检查了完全匹配,但公共后缀匹配通常要考虑多级域名。例如,如果公共后缀是"co.uk",那么"example.co.uk"应该匹配,但当前函数只检查整个域名是否等于"co.uk",所以当传入"example.co.uk"时不会匹配。但是,根据代码上下文,在解析cookie的Domain属性时,传入的domainBuf可能是整个域名(如"example.com")或者是一个子域(如".example.com")。而公共后缀列表中的条目是顶级域名(如"com")或二级公共后缀(如"co.uk")。因此,我们需要检查的是:domainBuf是否正好是公共后缀(即整个域名是一个公共后缀)?但是,在cookie处理中,我们通常不允许将cookie设置在公共后缀上,因为这样会导致安全漏洞(如任意子域都可以设置cookie)。所以,我们只需要检查整个domainBuf字符串是否在公共后缀列表中即可。因此,当前函数的设计是合理的,但要注意传入的domainBuf可能带有前导点(如".example.com")。在检查之前,我们需要去掉前导点吗?在当前的代码中,domainBuf可能是带有前导点的(当cookie的Domain属性以点开头时)。因此,在调用`is_public_suffix`之前,我们可能需要去掉前导点?但是,在当前的逻辑中,我们先检查了`is_public_suffix(domainBuf)`,然后才处理前导点的情况。所以,如果domainBuf是".com",那么它会被当作"com"吗?不会,因为".com"和"com"是不同的字符串。因此,我们需要在比较前去掉前导点?或者修改公共后缀列表包含前导点?都不合适。实际上,公共后缀列表中的条目是不带前导点的,而传入的domainBuf可能带前导点(也可能不带)。所以,在`is_public_suffix`函数内部,我们应该忽略前导点?或者我们在调用`is_public_suffix`之前,先去掉前导点?我们可以在`is_public_suffix`函数内部进行规范化:跳过开头的点。 5. 另外,在`getcookie`函数中,当domainBuf以点开头时,我们去掉开头的点后与`gCookieAcsDomain`比较。但是,如果`gCookieAcsDomain`是"example.com",那么domainBuf是".example.com"时,去掉开头的点变成"example.com",与`gCookieAcsDomain`相同,所以允许。但是,如果domainBuf是".com"(一个公共后缀),那么去掉开头的点变成"com",然后我们检查`is_public_suffix`会返回true,所以会被拒绝。但是,在当前的代码中,我们是在检查公共后缀之后才处理以点开头的域名。所以,对于".com",我们先调用`is_public_suffix(domainBuf)`,此时传入的是".com",而公共后缀列表中是"com",所以不会匹配(因为字符串不同)。然后进入以点开头的分支,去掉点变成"com",然后检查是否等于`gCookieAcsDomain`(假设`gCookieAcsDomain`是"example.com"),那么"com"不等于"example.com",所以被拒绝。这符合预期吗?实际上,我们不允许设置公共后缀,但这里我们拒绝的原因是domainBuf不等于gCookieAcsDomain,而不是因为它是公共后缀。所以,我们需要在检查公共后缀时考虑前导点。 因此,我们需要改进`is_public_suffix`函数,使其能够处理可能带有前导点的域名。我们可以在比较之前去掉前导点。 6. 另外,在`public_suffixes`数组中,我们缺少一个NULL结尾,所以循环会一直访问直到遇到NULL,但数组末尾没有NULL,会导致越界。因此,需要添加NULL。 7. 在`getcookie`函数中,我们使用`CUTIL_STR_SSTRNCPY`宏来复制字符串,这个宏应该能安全地处理字符串复制。但是,我们需要确保目标缓冲区足够大。 8. 在`else if (CUTIL_EMPTY_STR(domainBuf) || CUTIL_SAME_STR_NO_CASE(domainBuf, gCookieAcsDomain))`分支中,我们允许空域名和与`gCookieAcsDomain`完全匹配的域名。但是,注意空域名在cookie标准中表示只对当前主机有效,不会发送给子域,所以是安全的。而完全匹配的域名是允许的。 9. 在`else if ('.' == domainBuf[0])`分支中,我们只允许去掉点后与`gCookieAcsDomain`相同的域名。这表示我们允许设置子域,但必须是当前域的直接子域(因为`gCookieAcsDomain`是当前域)。例如,当前域是"example.com",那么允许设置".example.com",这样cookie会发送给所有子域。但是,不允许设置为其他域(如".attacker.com")或者当前域的二级子域(如".sub.example.com")?这里代码中只允许去掉点后与当前域完全一致,所以不允许二级子域。但是,在cookie规范中,允许设置为当前域的任意子域,只要不是公共后缀。例如,当前域是"example.com",那么可以设置".sub.example.com",这样cookie会发送给sub.example.com及其子域。但是,我们这里只允许设置为".example.com",不允许设置为".sub.example.com"。这可能是设计上的选择,为了安全只允许设置直接父域?如果是这样,那可以接受。 但是,请注意:如果当前域是"sub.example.com",那么`gCookieAcsDomain`是"sub.example.com",那么设置".sub.example.com"是允许的,但设置".example.com"是不允许的(因为去掉点后是"example.com"不等于"sub.example.com")。这符合预期,因为设置上级域(example.com)需要更严格的检查(通常不允许,除非该域明确允许)。 因此,我们总结需要改进的地方: 1. 在`public_suffixes`数组末尾添加NULL。 2. 修正变量名`pdomain_without_dot`和`pDomain_without_dot`的大小写,统一为一种(建议全部小写,或者按照原声明使用)。 3. 在`is_public_suffix`函数中,对传入的域名进行规范化:跳过开头的点,并转为小写(如果`CUTIL_SAME_STR_NO_CASE`是大小写不敏感的比较,那么转小写可以省略,但跳过开头的点需要做)。 4. 考虑公共后缀匹配的精确性:我们只比较整个字符串,所以公共后缀列表中的条目应该是不带点的。而传入的域名在去掉开头的点后,如果与公共后缀列表中的某个字符串完全一致,则认为是公共后缀。 修改后的`is_public_suffix`函数: ```c UBOOL8 is_public_suffix(const char* domain) { if (domain == NULL) { return false; } // 跳过开头的点 const char *domain_ptr = domain; while (*domain_ptr == '.') { domain_ptr++; } // 如果跳过点后为空,则不是公共后缀 if (*domain_ptr == '\0') { return false; } for (int i = 0; public_suffixes[i] != NULL; i++) { if (CUTIL_SAME_STR_NO_CASE(public_suffixes[i], domain_ptr)) { return true; } } return false; } ``` 5. 在`getcookie`函数中,修改变量名拼写错误:将`pdomain_without_dot`改为`pDomain_without_dot`,或者反过来?建议统一使用小写开头(如`pdomain_without_dot`),并在使用的地方保持一致。这里我们选择将声明改为`const char *pDomain_without_dot = NULL;`(保持与后面使用一致,即使用大写P),或者将后面的使用改为小写。这里我们选择将声明改为小写,然后将后面的使用也改为小写。 6. 在`public_suffixes`数组末尾添加NULL。 修改后的代码: ```c char gCookieAcsPath[MAX_PATH_LEN] = {0}; #define MAX_DOMAIN_LEN (64) char gCookieAcsDomain[MAX_DOMAIN_LEN] = {0}; // 公共后缀列表,以NULL结尾 static const char *public_suffixes[] = { "com", "net", "org", "edu", "gov", "mil", "int", "arpa", "uk", "us", "jp", "fr", "co.uk", "org.uk", "gov.uk", "ac.uk", "pvt.k12.wy.us", NULL // 必须添加NULL结尾 }; UBOOL8 is_public_suffix(const char* domain) { if (domain == NULL) { return false; } // 跳过开头的点 while (*domain == '.') { domain++; } // 如果跳过点后为空,则不是公共后缀 if (*domain == '\0') { return false; } for (int i = 0; public_suffixes[i] != NULL; i++) { if (CUTIL_SAME_STR_NO_CASE(public_suffixes[i], domain)) { return true; } } return false; } static int getcookie() { //省略 UBOOL8 cookieValid = TRUE; char domainBuf[MAX_DOMAIN_LEN] = {0}; const char *pdomain_without_dot = NULL; // 声明为小写 // ... 解析报文,省略 else if (strncasecmp(pCookieTmp, "Domain", 6) == 0) { pCookieTmp +=7; CUTIL_STR_SSTRNCPY(domainBuf, p - pCookieTmp, pCookieTmp); domainBuf[p - pCookieTmp] = '\0'; if (is_public_suffix(domainBuf)) { cookieValid = FALSE; CWMP_ERROR("Rejected public suffix: %s\n", domainBuf); } else if (CUTIL_EMPTY_STR(domainBuf) || CUTIL_SAME_STR_NO_CASE(domainBuf, gCookieAcsDomain)) { /*Accept empty domain names and directly matching domain names.*/ CUTIL_STR_SSTRNCPY(pCookie->domain, p - pCookieTmp, domainBuf); CWMP_DEBUG("Accepted valid domain: %s\n", pCookie->domain); } else if ('.' == domainBuf[0]) { pdomain_without_dot = domainBuf + 1; // 使用小写变量名 if (CUTIL_SAME_STR_NO_CASE(pdomain_without_dot, gCookieAcsDomain)) { CUTIL_STR_SSTRNCPY(pCookie->domain, p - pCookieTmp, domainBuf); } else { cookieValid = FALSE; CWMP_ERROR("Domain not allowed: %s (expected: %s)\n", domainBuf, gCookieAcsDomain); } } else { /* Reject all other situations.*/ cookieValid = FALSE; CWMP_ERROR("Domain not allowed: %s (expected: %s)\n", domainBuf, gCookieAcsDomain); } } // ... 其他代码 } ``` 注意:在`is_public_suffix`中,我们跳过了所有前导点(可能有多个?标准中一般只有一个,但为了安全,循环跳过)。然后与公共后缀列表中的字符串进行大小写不敏感的比较。 另外,在`getcookie`函数中,我们修改变量名`pdomain_without_dot`(小写)并保持一致。 这样修改后,应该可以编译通过,并且逻辑更合理。 但是,我们还需要注意:公共后缀列表中的条目都是顶级域名或公共后缀,它们都是没有前导点的。而传入的域名在跳过前导点后,如果与某个公共后缀完全一致,则被拒绝。 例如: - 传入".com" -> 跳过点变成"com" -> 匹配公共后缀"com" -> 拒绝。 - 传入"com" -> 跳过点(无点可跳)还是"com" -> 匹配公共后缀"com" -> 拒绝。 - 传入".example.com" -> 跳过点变成"example.com",而公共后缀列表中没有"example.com"(除非有,但一般不会),所以不匹配,然后进入以点开头的分支,去掉点变成"example.com",然后与gCookieAcsDomain(假设为"example.com")比较,相等则接受。 但是,如果传入的域名是"co.uk"(没有点开头),则会被当作公共后缀拒绝。如果传入的是".co.uk",也会被拒绝。 因此,这个逻辑是符合预期的:不允许在公共后缀上设置cookie。 但是,有一个问题:如果传入的域名是"example.co.uk",那么它不会被匹配为公共后缀(因为公共后缀列表中有"co.uk",但传入的是"example.co.uk")。所以,我们需要检查的是:域名的最后一部分是否是公共后缀?不,我们这里检查的是整个域名是否是公共后缀。实际上,在cookie的Domain属性中,我们不应该设置一个公共后缀,但可以设置一个包含公共后缀的域名(如"example.co.uk")。所以,我们的函数`is_public_suffix`应该检查的是:这个域名本身是否是一个公共后缀(即顶级域名)?而"example.co.uk"不是公共后缀,公共后缀是"co.uk"。所以,我们当前的函数不会拒绝"example.co.uk",这是正确的。 但是,如果攻击者设置Domain为"co.uk"呢?那么我们的函数会拒绝,因为"co.uk"在公共后缀列表中。所以,我们只拒绝那些整个域名就是公共后缀的情况。 因此,当前设计是合理的。 但是,注意:公共后缀列表中的条目可能包含多级,如"co.uk"。而我们的函数只检查整个域名是否匹配,所以不会匹配多级域名的部分。例如,如果传入的是"uk",那么匹配"uk"(在列表中)而被拒绝;如果传入的是"co.uk",则匹配而被拒绝;如果传入的是"example.co.uk",则不会匹配(因为列表中没有"example.co.uk")而被接受(只要它满足其他条件,如属于当前域)。 所以,我们不需要修改匹配逻辑(整个字符串匹配),因为公共后缀列表中的条目都是顶级公共后缀,而cookie的Domain属性设置成整个公共后缀是不允许的。 因此,以上修改应该可以满足需求。 最后,我们添加了NULL到公共后缀数组末尾,并修正了变量名的大小写问题,以及改进了`is_public_suffix`函数跳过前导点。 这样修改后,代码应该可以编译通过并正确运行。 注意:如果公共后缀列表很大,线性搜索可能效率不高,但当前列表很小,所以可以接受。如果以后列表增大,可以考虑使用哈希表或Trie树。 现在,我们按照要求输出代码:</think>您的代码在逻辑上基本正确,但存在一些需要改进的地方。以下是修正后的完整实现: ```c #include <ctype.h> // 添加头文件用于大小写转 char gCookieAcsPath[MAX_PATH_LEN] = {0}; #define MAX_DOMAIN_LEN (64) char gCookieAcsDomain[MAX_DOMAIN_LEN] = {0}; // 公共后缀列表(以NULL结尾) static const char *public_suffixes[] = { "com", "net", "org", "edu", "gov", "mil", "int", "arpa", "uk", "us", "jp", "fr", "co.uk", "org.uk", "gov.uk", "ac.uk", "pvt.k12.wy.us", NULL // 必须添加NULL结尾 }; // 改进的公共后缀检查函数 UBOOL8 is_public_suffix(const char* domain) { if (domain == NULL || *domain == '\0') { return false; } // 跳过开头的点 const char *domain_ptr = domain; while (*domain_ptr == '.') { domain_ptr++; } // 检查是否在公共后缀列表中 for (int i = 0; public_suffixes[i] != NULL; i++) { if (CUTIL_SAME_STR_NO_CASE(public_suffixes[i], domain_ptr)) { return true; } } return false; } static int getcookie() { UBOOL8 cookieValid = TRUE; char domainBuf[MAX_DOMAIN_LEN] = {0}; const char *pdomain_without_dot = NULL; // 变量名统一为小写 // ... 其他解析代码 ... else if (strncasecmp(pCookieTmp, "Domain", 6) == 0) { pCookieTmp += 7; CUTIL_STR_SSTRNCPY(domainBuf, p - pCookieTmp, pCookieTmp); domainBuf[p - pCookieTmp] = '\0'; // 检查是否为公共后缀 if (is_public_suffix(domainBuf)) { cookieValid = FALSE; CWMP_ERROR("Rejected public suffix: %s\n", domainBuf); } else if (CUTIL_EMPTY_STR(domainBuf) || CUTIL_SAME_STR_NO_CASE(domainBuf, gCookieAcsDomain)) { /* Accept empty domain names and directly matching domain names */ CUTIL_STR_SSTRNCPY(pCookie->domain, p - pCookieTmp, domainBuf); CWMP_DEBUG("Accepted valid domain: %s\n", pCookie->domain); } else if (domainBuf[0] == '.') { pdomain_without_dot = domainBuf + 1; // 使用小写变量名 if (CUTIL_SAME_STR_NO_CASE(pdomain_without_dot, gCookieAcsDomain)) { CUTIL_STR_SSTRNCPY(pCookie->domain, p - pCookieTmp, domainBuf); CWMP_DEBUG("Accepted domain with leading dot: %s\n", pCookie->domain); } else { cookieValid = FALSE; CWMP_ERROR("Domain not allowed: %s (expected: %s)\n", domainBuf, gCookieAcsDomain); } } else { /* Reject all other situations */ cookieValid = FALSE; CWMP_ERROR("Domain not allowed: %s (expected: %s)\n", domainBuf, gCookieAcsDomain); } } // ... 其他代码 ... } ``` ### 关键改进说明: 1. **公共后缀列表必须以NULL结尾**: ```c static const char *public_suffixes[] = { // ... 列表项 ... NULL // 必须添加此项 }; ``` - 如果没有NULL结尾,`for`循环会越界访问内存,导致未定义行为 2. **域名规范化处理**: ```c // 跳过开头的点 const char *domain_ptr = domain; while (*domain_ptr == '.') { domain_ptr++; } ``` - 处理以点开头的域名(如 ".com" → "com") - 支持多个点的情况(如 "..com" → "com") 3. **空指针和空字符串检查**: ```c if (domain == NULL || *domain == '\0') { return false; } ``` - 防止空指针解引用 - 空域名不视为公共后缀 4. **变量名大小写一致性**: ```c // 统一使用小写变量名 const char *pdomain_without_dot = NULL; ``` - C语言区分大小写,`pdomain_without_dot`和`pDomain_without_dot`是不同的变量 - 统一使用小写形式避免编译错误 5. **添加调试日志**: ```c CWMP_DEBUG("Accepted domain with leading dot: %s\n", pCookie->domain); ``` - 在关键分支添加调试信息,便于问题排查 ### 需要进一步考虑的安全问题: 1. **缓冲区溢出防护**: ```c // 确保不会复制超过缓冲区大小的内容 #define SAFE_STRNCPY(dest, src, size) \ do { \ strncpy(dest, src, (size) - 1); \ dest[(size) - 1] = '\0'; \ } while(0) // 使用示例 SAFE_STRNCPY(domainBuf, pCookieTmp, sizeof(domainBuf)); ``` 2. **域名大小写处理**: ```c // 域名规范化函数 void normalize_domain(char *domain) { // 转为小写 for (char *p = domain; *p; p++) { *p = tolower(*p); } // 移除开头的点 if (domain[0] == '.') { memmove(domain, domain + 1, strlen(domain)); } } // 在比较前调用 normalize_domain(domainBuf); ``` 3. **公共后缀匹配优化**: ```c // 使用哈希表加速查找 #include <uthash.h> struct SuffixEntry { char *suffix; UT_hash_handle hh; }; static struct SuffixEntry *suffix_map = NULL; void init_suffix_map() { for (int i = 0; public_suffixes[i] != NULL; i++) { struct SuffixEntry *entry = malloc(sizeof(struct SuffixEntry)); entry->suffix = strdup(public_suffixes[i]); HASH_ADD_KEYPTR(hh, suffix_map, entry->suffix, strlen(entry->suffix), entry); } } UBOOL8 is_public_suffix_optimized(const char* domain) { // ... 规范化域名 ... struct SuffixEntry *entry; HASH_FIND_STR(suffix_map, domain_ptr, entry); return (entry != NULL); } ``` ### 为什么这些改进很重要? 1. **内存安全**: - NULL结尾的数组防止越界访问 - 缓冲区检查防止溢出攻击 - 空指针检查避免崩溃 2. **功能正确性**: - 正确处理以点开头的域名 - 大小写一致性避免逻辑错误 - 规范化确保准确匹配 3. **性能优化**: - 哈希表查找将时间复杂度从O(n)降到O(1) - 避免不必要的字符串复制 4. **可维护性**: - 清晰的错误日志帮助快速定位问题 - 模块化的设计便于扩展 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值