SVF Saber的实现

news2024/11/19 8:30:45

SVF Saber

  • 1.基本原理
  • 2.API类型定义
  • 3.Memory Leak Checker
    • 3.1.示例
    • 3.2.初始化
    • 3.3.程序切片
    • 3.4.路径约束求解
    • 3.5.报告错误
  • 4.总结
  • 5.参考文献

Saber是一个静态漏洞检测器,最初集成到open64中,现已集成到SVF中,主要检测内存泄漏,DoubleFree,操作(检查open/close是否一致)。

1.基本原理

关于内存泄漏检测可以参考Saber paper 2.6章,主要的思路是

  • 进行指针分析和value flow分析建立sparse value flow graph

  • 基于sparse value flow graph(svfg)进行some path分析,即在value flow graph上进行source-sink分析(从 mallocfree 的svfg路径)。如果一个source点不能在svfg上到达sink点,那么表示该 malloc 从没有被 free,一定存在内存泄漏,报出 NeverFree 错误。

  • 接下来进行all path分析,主要目的是确保在每个control-flow路径上source点都能达到sink点,如果存在一个control-flow路径source点没有到达sink点,那么报出 PartialLeak 错误。

关于all path分析,作者定义了如下概念:

  • S: S s r c S_{src} Ssrc 表示source点在value flow graph上可达的所有sink点

  • vfpaths(src, tgt): 从source点 src 到sink点 tgt 的所有value- flow路径(可能不止有1条路径)

  • vfguard(P): value flow路径 P 的路径条件, v f g u a r d ( P ) = ⋀ ( p , q ) ∈ P c f g u a r d ( p , q ) vfguard(P) = \bigwedge_{(p, q) \in P} cfguard(p, q) vfguard(P)=(p,q)Pcfguard(p,q)(p, q) 是一个value-flow(def-use)路径。

  • FREED(src): F R E E D ( s r c ) = ⋁ t g t ∈ S s r c ( ⋁ P ∈ v f p a t h s ( s r c , t g t ) v f g u a r d ( P ) ) FREED(src) = \bigvee\limits_{tgt \in S_{src}}(\bigvee_{P \in vfpaths(src, tgt)} vfguard(P)) FREED(src)=tgtSsrc(Pvfpaths(src,tgt)vfguard(P)) F R E E D ( s r c ) ≡ t r u e FREED(src) \equiv true FREED(src)true 表示 src 分配的内存一定会被free,反之则表示可能在某些路径存在泄漏,需要报出 PartialLeak 错误。前面的公式表示对应一对 (src, tgt),如果这对中间有一个value flow path对应的路径条件一定能得到满足,那么 src 不管沿着哪条control-flow路径都能到达 tgt。对于一个 src 点,在value-flow graph中所有可达的sink点中,只要有1个一定可达,那么该 src 点一定会被 free

  • cfpaths(p, q): 表示def点 p 到use点 q 之间所有的控制流路径。

  • cfguard(p, q) 表示def点 p 到use点 q 之间的路径条件,为 true 表示在控制流图上 p 一定能到达 q c f g u a r d ( p , q ) = ⋁ Q ∈ c f p a t h s ( p , q ) p g u a r d ( Q ) cfguard(p, q) = \bigvee_{Q \in cfpaths(p, q)} pguard(Q) cfguard(p,q)=Qcfpaths(p,q)pguard(Q)Q 为一条控制流路径。

  • pguard(Q) 表示控制流路径 Q 的路径条件, p g u a r d ( Q ) = ⋀ e ∈ E ( Q ) e g u a r d ( e ) pguard(Q) = \bigwedge_{e \in E(Q)} eguard(e) pguard(Q)=eE(Q)eguard(e)E(Q)Q 上的边集合, eguard(e) 为边 e 对应的约束条件。

2.API类型定义

Saber的入口文件是Saber.cpp,可以看到:

  • FileChecker 类实现对File操作的检查。

  • DoubleFreeChecker 类实现对DoubleFree的检查。

  • LeakChecker 类实现对Use-After-Free和其它泄漏漏洞的检查。

其中 DoubleFreeCheckerFileChecker 继承自LeakChecker,因此先看看 LeakChecker

首先Saber中定义了4种API类型,参考SaberCheckerAPI.h,具体的API名称在SaberCheckerAPI.cpp中可查看,包括

  • SaberCheckerAPI::CK_ALLOC:包括 malloc, alloc 等,isMemAlloc 函数判断给定API是否属于 Alloc

  • SaberCheckerAPI::CK_FREE:包括 free 等函数,isMemDealloc 判断给定API是否属于 CK_FREE

  • SaberCheckerAPI::CK_FOPEN:包括 fopen 等函数,isFOpen 判断给定API是否属于 CK_FOPEN

  • SaberCheckerAPI::CK_FCLOSE:包括 fclose, pclose 等函数,isFClose 判断给定API是否属于 CK_FCLOSE

同时定义了一个 StringMap 类变量 TDAPIMap 来将函数名映射为 type。

3.Memory Leak Checker

执行Checker调用的函数顺序如下:

main -> LeakChecker:runOnModule -> SrcSnkDDA::analyze

分析的主要过程是在 SrcSnkDDA::analyze 中实现,而LeakChecker 主要重新定义了 srcsink 相关信息。

3.1.示例

取自saber论文中的样例,改写之后如下

#include <stdio.h>
#include <stdlib.h>


char** readBuf() {
   char** mBuf = (char**)malloc(2*sizeof(char*)); //o
   *mBuf = (char*)malloc(10*sizeof(char)); //o1
   scanf("%6s", *mBuf);  //... (write into **mBuf);
   return mBuf;
}

void freeBuf(char** fBuf) {
    char* z = *fBuf;
    free(z);
    free(fBuf);
}

int main() {
    for (int i = 0; i < 100; ++i) {
        char** buf = readBuf();
        char* tmp = *buf;
        if (*tmp != '\n')
            printf("%s\n", tmp);
        else
            continue;
        freeBuf(buf);
    }
    return 0;
}

LLVM IR如下

; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "arm64-apple-macosx12.0.0"

@.str = private unnamed_addr constant [4 x i8] c"%6s\00", align 1
@.str.1 = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1

; Function Attrs: noinline nounwind optnone ssp uwtable
define dso_local i8** @readBuf() #0 {
entry:
  %mBuf = alloca i8**, align 8
  %call = call i8* @malloc(i64 16) #3
  %0 = bitcast i8* %call to i8**
  store i8** %0, i8*** %mBuf, align 8
  %call1 = call i8* @malloc(i64 10) #3
  %1 = load i8**, i8*** %mBuf, align 8
  store i8* %call1, i8** %1, align 8
  %2 = load i8**, i8*** %mBuf, align 8
  %3 = load i8*, i8** %2, align 8
  %call2 = call i32 (i8*, ...) @scanf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i8* %3)
  %4 = load i8**, i8*** %mBuf, align 8
  ret i8** %4
}

; Function Attrs: allocsize(0)
declare i8* @malloc(i64) #1

declare i32 @scanf(i8*, ...) #2

; Function Attrs: noinline nounwind optnone ssp uwtable
define dso_local void @freeBuf(i8** %fBuf) #0 {
entry:
  %fBuf.addr = alloca i8**, align 8
  %z = alloca i8*, align 8
  store i8** %fBuf, i8*** %fBuf.addr, align 8
  %0 = load i8**, i8*** %fBuf.addr, align 8
  %1 = load i8*, i8** %0, align 8
  store i8* %1, i8** %z, align 8
  %2 = load i8*, i8** %z, align 8
  call void @free(i8* %2)
  %3 = load i8**, i8*** %fBuf.addr, align 8
  %4 = bitcast i8** %3 to i8*
  call void @free(i8* %4)
  ret void
}

declare void @free(i8*) #2

; Function Attrs: noinline nounwind optnone ssp uwtable
define dso_local i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  %i = alloca i32, align 4
  %buf = alloca i8**, align 8
  %tmp = alloca i8*, align 8
  store i32 0, i32* %retval, align 4
  store i32 0, i32* %i, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
  %0 = load i32, i32* %i, align 4
  %cmp = icmp slt i32 %0, 100
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  %call = call i8** @readBuf()
  store i8** %call, i8*** %buf, align 8
  %1 = load i8**, i8*** %buf, align 8
  %2 = load i8*, i8** %1, align 8
  store i8* %2, i8** %tmp, align 8
  %3 = load i8*, i8** %tmp, align 8
  %4 = load i8, i8* %3, align 1
  %conv = sext i8 %4 to i32
  %cmp1 = icmp ne i32 %conv, 10 # 这个指令对应 *tmp != '\n',是all path分析中唯一出现的路径约束
  br i1 %cmp1, label %if.then, label %if.else

if.then:                                          ; preds = %for.body
  %5 = load i8*, i8** %tmp, align 8
  %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0), i8* %5)
  br label %if.end

if.else:                                          ; preds = %for.body
  br label %for.inc

if.end:                                           ; preds = %if.then
  %6 = load i8**, i8*** %buf, align 8
  call void @freeBuf(i8** %6)
  br label %for.inc

for.inc:                                          ; preds = %if.end, %if.else
  %7 = load i32, i32* %i, align 4
  %inc = add nsw i32 %7, 1
  store i32 %inc, i32* %i, align 4
  br label %for.cond, !llvm.loop !7

for.end:                                          ; preds = %for.cond
  ret i32 0
}

declare i32 @printf(i8*, ...) #2

attributes #0 = { noinline nounwind optnone ssp uwtable "disable-tail-calls"="false" "frame-pointer"="non-leaf" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-a12" "target-features"="+aes,+crc,+crypto,+fp-armv8,+fullfp16,+lse,+neon,+ras,+rcpc,+rdm,+sha2,+v8.3a,+zcm,+zcz" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { allocsize(0) "disable-tail-calls"="false" "frame-pointer"="non-leaf" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-a12" "target-features"="+aes,+crc,+crypto,+fp-armv8,+fullfp16,+lse,+neon,+ras,+rcpc,+rdm,+sha2,+v8.3a,+zcm,+zcz" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="non-leaf" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-a12" "target-features"="+aes,+crc,+crypto,+fp-armv8,+fullfp16,+lse,+neon,+ras,+rcpc,+rdm,+sha2,+v8.3a,+zcm,+zcz" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { allocsize(0) }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
!llvm.ident = !{!6}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, !"branch-target-enforcement", i32 0}
!2 = !{i32 1, !"sign-return-address", i32 0}
!3 = !{i32 1, !"sign-return-address-all", i32 0}
!4 = !{i32 1, !"sign-return-address-with-bkey", i32 0}
!5 = !{i32 7, !"PIC Level", i32 2}
!6 = !{!"clang version 12.0.0"}
!7 = distinct !{!7, !8}
!8 = !{!"llvm.loop.mustprogress"}

value flow graph如下(没有截取全部,只是一些关键部分),图中红框为识别的source点,蓝框为sink点(sink点不是 call 指令而是对应的加载参数指令,可能是因为 call 指令并没有出现在value flow graph中)。

第一部分
请添加图片描述第二部分

请添加图片描述

3.2.初始化

SrcSnkDDA::analyze 首先调用 SrcSnkDDA::initialize

void SrcSnkDDA::initialize(SVFModule* module)
{
	PAGBuilder builder;
	PAG* pag = builder.build(module);

    AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(pag);
    svfg =  memSSA.buildPTROnlySVFG(ander);
    setGraph(memSSA.getSVFG());
    ptaCallGraph = ander->getPTACallGraph();
    //AndersenWaveDiff::releaseAndersenWaveDiff();
    /// allocate control-flow graph branch conditions
    getPathAllocator()->allocate(getPAG()->getModule());

    initSrcs();
    initSnks();
}

主要流程是:

Created with Raphaël 2.3.0 建立程序赋值图(PAG Wave Andersen 指针分析 Value Flow Graph建立 初始化Src点 初始化Sink点 结束

之后就是初始化source和sink点,LeakChecker, FileChecker 和 DoubleFreeChecker 都是基于相关API调用识别source和sink点。初始化source和sink集合的方法也相同,主要的区别在于关注的API类型不同,具体的差别在 isSinkLikeFUnisSourceLikeFun

先看LeakChecker 的 isSourceLikeFun 和 isSinkLikeFun。LeakCheckerCK_ALLOC 视为 src 点而将 CK_FREE 类视为 sink 点。

初始化src 点的代码可以参考 LeakChecker::initSrcs 函数。主要是将 malloc 类的函数调用添加到 src 点中,如果存在多层嵌套情况,比如

char *customizedMalloc() {
	char *buf = (char*)malloc(10);
	// ... read content to buf
	return buf;
}

int main() {
    char *buf = customizedMalloc();
    return 0;
}

那么被添加到 src 集合的是 customizedMalloc 调用而不是 malloc

初始化 sink 点的代码可以参考 LeakChecker::initSnks,这里主要关注 free 类函数调用,并将对应参数加载指令(不是 call 指令)添加到 sink 集合。

%4 = bitcast i8** %3 to i8*
call void @free(i8* %4)

上述IR中,加载参数的指令是 %4 = bitcast i8** %3 to i8*, 因此该指令会被添加为 sink 点而不是 free 函数调用。这么设计的一个原因可能是对应 free 调用并不会出现在value flow graph中,取而代之的是参数加载指令(这点是本人推测,目前对value flow graph不是很熟,欢迎大佬指正)。

3.3.程序切片

这部分即some-path分析,代码参考SrcSnkDDA::analyze, 主体代码如下:

for (SVFGNodeSetIter iter = sourcesBegin(), eiter = sourcesEnd(); iter != eiter; ++iter){
    setCurSlice(*iter); // 初始化该source点对应的切片
      
    ContextCond cxt;
    DPIm item((*iter)->getId(),cxt);
    forwardTraverse(item); // 从source点开始前向遍历

    /// do not consider there is bug when reaching a global SVFGNode
    /// if we touch a global, then we assume the client uses this memory until the program exits.
    if (getCurSlice()->isReachGlobal()) // 如果涉及到全局变量,那么不认为会出现bug
        DBOUT(DSaber, outs() << "Forward analysis reaches globals for slice:" << (*iter)->getId() << ")\n");
       
    else {
        // 前向遍历会将从source点可达的所有value flow graph结点添加到forwardSlice集合中,下面的for循环会遍历所有source点可达的sink点
        for (SVFGNodeSetIter sit = getCurSlice()->sinksBegin(), esit = getCurSlice()->sinksEnd(); sit != esit; ++sit) {
            ContextCond cxt;
            DPIm item((*sit)->getId(),cxt);
            // 从sink点开始反向遍历
            backwardTraverse(item);
        }

        if(_curSlice->AllPathReachableSolve())
            _curSlice->setAllReachable();

   }

   reportBug(getCurSlice());
}

每个切片都会包含 forwardslice, backwardslice, sinks, partialReachable, fullReachable, reachGlob 等局部变量。其中

  • partialReachable 表示source点沿着某些控制流路径可能到达sink点。

  • fullReachable 表示source点沿着每条控制流路径都能到达sink点(也就是source点一定会经过sink点)。

  • reachGlob 表示这个切片是否会到达局部变量。

forwardTraversebackwardTraverse 都是采用worklist算法前向/反向遍历从指定结点可达的所有结点,需要关注的有SrcSnkDDA::FWProcessCurNode 和 SrcSnkDDA::BWProcessCurNode。这2个函数分别是前向/反向对每个结点的处理过程。

  • 前向遍历时需要将每个访问的结点添加到 forwardslice,如果遍历的结点属于sink点,那么再将它添加到 sinks 集合中。

  • 反向遍历时如果该结点在 forwardslice 集合中,那么将它添加到 backwardslice 集合中。

用上面示例对应的部分value flow graph来说明

请添加图片描述
图中蓝框87号结点为source点,最右下角绿框中90号为sink点,那么

  • 蓝框 + 红框部分为 forwardslice 的内容。

  • 蓝框 + 3个绿框为 backwardslice 中的内容。

可以看到slice的主要目的是找到从source点到sink点的路径, backwardslice 中不应有除了路径以外的结点。

3.4.路径约束求解

这一部分即all-path分析,主要由上面代码中 _curSlice->AllPathReachableSolve() 完成,ProgSlice::AllPathReachableSolve 主要是对该 slicebackwardslice 上的所有结点进行约束求解,看看是否所有的控制流路径都能得到满足。

主要代码如下(删除了一些代码,部分打了注释)

// 这个函数主要进行路径约束合并
bool CustomizedProgSlice::AllPathReachableSolve()
{
    const SVFGNode* source = getSource();
    VFWorkList worklist;
    worklist.push(source);
    /// 将初始路径约束设置为true
    setVFCond(source,getTrueCond());

    while(!worklist.empty())
    {
        const SVFGNode* node = worklist.pop();
        setCurSVFGNode(node);
        Condition* cond = getVFCond(node);
        for(SVFGNode::const_iterator it = node->OutEdgeBegin(), eit = node->OutEdgeEnd(); it!=eit; ++it)
        {
            const SVFGEdge* edge = (*it);
            const SVFGNode* succ = edge->getDstNode();
            // 只对backwardslice上的结点进行求解
            if(inBackwardSlice(succ))
            {
                Condition* vfCond = nullptr;
                // 将value flow node映射为basic block node,每个value flow node结点都保存了一个指针类型局部变量指向对应ICFG结点,而ICFG又保存了一个指针指向basic block
                const BasicBlock* nodeBB = getSVFGNodeBB(node);
                const BasicBlock* succBB = getSVFGNodeBB(succ);

                errs() << "current SVFG node: " << node->toString() << "\n";
                errs() << "succeed SVFG node: " << succ->toString() << "\n";
                errs() << "current basic block\n";
                nodeBB->dump();
                errs() << "succeed basic block\n";
                succBB->dump();


                /// clean up the control flow conditions for next round guard computation
                clearCFCond();
							
							 // 根据不同的情况计算当前value flow edge对应的路径约束条件 
                if(edge->isCallVFGEdge())
                {
                    vfCond = ComputeInterCallVFGGuard(nodeBB,succBB, getCallSite(edge)->getParent());
                }
                else if(edge->isRetVFGEdge())
                {
                    vfCond = ComputeInterRetVFGGuard(nodeBB,succBB, getRetSite(edge)->getParent());
                }
                else
                    vfCond = ComputeIntraVFGGuard(nodeBB,succBB); // cfguard(node, succ) = pguard(Q1) or ... pguard(Qn)

                // vfguard(P) = vfguard(P-) and cfguard(node, succ),
                Condition* succPathCond = condAnd(cond, vfCond);
                // cfguard(p, q) = pguard(Q1) or ... pguard(Qm)
                if(setVFCond(succ,  condOr(getVFCond(succ), succPathCond) ))
                    worklist.push(succ);
            }

            DBOUT(DSaber, outs() << " node (" << node->getId()  <<
                                 ") --> " << "succ (" << succ->getId() << ") condition: " << getVFCond(succ) << "\n");
        }
    }

    return isSatisfiableForAll();
}

/*!
 * Solve by computing disjunction of conditions from all sinks (e.g., memory leak)
 * 路径约束求解
 */
bool CustomizedProgSlice::isSatisfiableForAll()
{

    Condition* guard = getFalseCond();
    // each it represents vfguard(src, it);
    for(SVFGNodeSetIter it = sinksBegin(), eit = sinksEnd(); it!=eit; ++it)
        guard = condOr(guard,getVFCond(*it));

    setFinalCond(guard);
    errs() << "final condition: " << guard->getExpr().to_string() << "\n";
    return pathAllocator->isAllPathReachable(guard);
}

在计算路径约束部分,不管是value-flow还是control-flow graph,路径上的每个结点都会映射一个约束条件,给定下面示例(结点是指令,边包含约束条件)

c1
c2
c3
c4
inst1
inst2
inst3
inst4
  • vfcond(inst1) = true

  • vfcond(inst2) = c1

  • vfcond(inst3) = c2

  • vfcond(inst4) = (c1 and c4) or (c2 and c3)

在Saber约束求解部分我并没有发现调用z3的代码,貌似saber会将每个路径约束计作 c1, c2 ..., cn 格式并做简单SAT求解,并不会进行复杂的约束求解。

上面出现的示例中唯一出现的约束条件是 %cmp1 = icmp ne i32 %conv, 10,在求解中会被转换成谓词 c1。这个约束条件在求解value flow edge store i8** %call, i8*** %buf, align 8 ----> %7 = load i8**, i8*** %buf, align 8会出现。这2条指令对应的basic block分别是

for.body:                                         ; preds = %for.cond
  %call = call i8** @readBuf()
  store i8** %call, i8*** %buf, align 8
  %1 = load i8**, i8*** %buf, align 8
  %2 = load i8*, i8** %1, align 8
  store i8* %2, i8** %tmp, align 8
  %3 = load i8*, i8** %tmp, align 8
  %4 = load i8, i8* %3, align 1
  %conv = sext i8 %4 to i32
  %cmp1 = icmp ne i32 %conv, 10
  br i1 %cmp1, label %if.then, label %if.else

if.end:                                           ; preds = %if.then
  %7 = load i8**, i8*** %buf, align 8
  call void @freeBuf(i8** %7)
  br label %for.inc

3.5.报告错误

参考LeakChecker::reportBug,如果 isAllPathReachable() == false && isSomePathReachable() == false,也就是一条从 mallocfree 的路径都不存在,那么报出 NeverFree 错误(个人感觉按照paper所说 isSomePathReachable 就可以报出了,不需要再做all-path分析了)。如果 isAllPathReachable() == false && isSomePathReachable() == true 那么表示存在某些控制流路径会导致泄漏,报出 PartialLeak 错误。

FileChecker 的原理类似,不过是将 malloc, free 替换成 fopen, fclose

上面示例会报出2个partial leak错误,对应的slice分别是2个示例value flow graph中从红框到蓝框的那条路径。

4.总结

这篇blog主要说明saber是如何基于sparse value flow graph进行source-sink分析查找memory leak漏洞,double free和file close的原理类似。尚未提到的内容有:

  • sparse value flow graph的建立过程,包括怎么映射到ICFG node。

  • all-path分析时候是如何进行路径条件求解的,这里只是看了一下代码发现不是调用z3进行约束求解,再dump了一下路径约束发现dump出来的是 c1 这种简化表达式,于是猜测可能只是将碰到的条件表达式简化成 ci 形式进行简单SAT求解,论文中提到的BDD或许是一种SAT求解的优化形式。

不过saber貌似可能对不同优化程度编译出来的IR会产生不同的结果,这个还得看看后续会如何改进。

5.参考文献

[1] Sui Y , Ye D , Xue J . Static memory leak detection using full-sparse value-flow analysis[C]// International Symposium on Software Testing and Analysis. ACM, 2012.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GAN的损失函数

1.GAN 在训练过程中&#xff0c;生成器和判别器的目标是相矛盾的&#xff0c;并且这种矛盾可以体现在判别器的判断准确性上。生成器的目标是生成尽量真实的数据&#xff0c;最好能够以假乱真、让判别器判断不出来&#xff0c;因此生成器的学习目标是让判别器上的判断准确性越来…

PCL点云处理之快速点特征直方图(FPFH)描述符(八十六)

PCL点云处理之快速点特征直方图(FPFH)描述符(八十六) 前言一、快速点特征直方图理论二、FPFH和PFH的区别二、实验过程1.代码2输入法线的NAN值检查用 OpenMP 加速 FPFH前言 对于具有 n 个点的给定点云 P,点特征直方图(见点特征直方图(PFH)描述符)的理论计算复杂度为 O (nk…

VueRouter路由的使用(上)

文章目录VueRouter路由的使用p21路由的原理_hash改变historyP22 认识vue-router路由的基本使用流程默认路径router-link路由懒加载路由的其他属性动态路由的基本匹配NotFound路由嵌套编程式导航VueRouter路由的使用 p21 路由的原理_hash改变 <!DOCTYPE html> <html …

JavaScript 运算符

文章目录JavaScript 运算符JavaScript 算术运算符JavaScript 赋值运算符用于字符串的 运算符对字符串和数字进行加法运算JavaScript 运算符 运算符 用于赋值。 运算符 用于加值。 运算符 用于给 JavaScript 变量赋值。 算术运算符 用于把值加起来。 实例 指定变量值&am…

Java基础学习笔记(十)—— 异常

异常1 异常概述2 异常处理方式2.1 JVM默认处理异常的方式2.2 throws方式处理异常2.3 throw抛出异常2.4 try-catch方式处理异常2.5 Throwable成员方法2.6 异常的练习3 自定义异常1 异常概述 异常&#xff1a;就是程序出现了不正常的情况。程序在执行的过程中&#xff0c;出现的…

Vivado综合设置之-gated_clock_conversion

本文验证-gated_clock_conversion设置为on或off时&#xff0c;给Schematic带来的差异。 -gated_clock_conversion设置为on时&#xff0c;用于移除门控时钟&#xff0c;门控时钟是由门电路而非专用时钟模块&#xff08;例如MMCM或PLL&#xff09;生成的时钟。 门控时钟会对设计…

Java-集合(3)

Vector集合类 1.Vector底层保存数据的也是一个对象数组&#xff1a;protected Object[] elementDate; 2.Vector是线程同步的&#xff0c;也就是线程安全Vactor的操作方法都带有synchronized修饰。以此可以进行安全线程保障&#xff0c;所以在开发中如果确认只有一个线程操作集…

dubbo学习笔记3(小d课堂)

dubbo高级特性 启动依赖检查 我们现在直接来启动我们的消费者&#xff1a; 它会报错。 我们 再去直接运行我们的消费者就不会报错。 我们也可以不在代码中去配置&#xff1a; 实际工作中比较建议使用这种方式。 dubbo.reference.check是配置所有的reference里的service都是f…

C语言:大小端

大小端 对于整型来说&#xff0c;内存中存放的是补码&#xff0c;补码是按大小端模式进行存储&#xff1a; 大端存储数据低位存在内存高地址&#xff0c;高位存在内存低地址小端存储数据低位存在内存低地址&#xff0c;高位存在内存高地址 记忆方法 小小小&#xff1a;数据 小…

前端组件库自定义主题切换探索-01

探索原因背景 首先自然是项目有需求&#xff0c;这是必须去做的原因 其次&#xff0c;是我们项目没有直接使用市面上现成的基于element-ui或者ant-design的第三方UI框架&#xff0c;比如avue&#xff0c;而是有着自己的UI组件库 第三&#xff0c;我们的组件库基于ant-design-v…

Win10下干净卸载VMware15.5

一、说明 虚拟机属于服务软件&#xff0c;在Windows10下卸载实属不易。下面请看我所总结的卸载文档。 二、如何彻底卸载VMware虚拟机 需要删除的部分很多&#xff0c;包括&#xff1a; 三、删除步骤一&#xff1a;需要禁用VM虚拟机服务 首先&#xff0c;因为VM的软件属于底层…

【自学Python】Python转义字符

文章来源嗨客网&#xff08;www.haicoder.net&#xff09; Python转义字符 Python转义字符教程 在编程中有一些字符是打不出来的&#xff0c;比如换行&#xff0c;提示音&#xff0c;下一个制表位等等&#xff0c;于是程序语言的开发者就设计了转义序列&#xff08;escape se…

4 机器学习之决策树

学习笔记自&#xff0c;慕课网 《Python3 入门人工智能》 https://https://coding.imooc.com/lesson/418.html#mid32776 决策树、异常检测、主成分分析 常用的分类方法&#xff1a; 逻辑回归的思路&#xff1a; 决策树的思路&#xff1a; 1. 决策树 1.1 ID3决策树&#x…

Java 防御XSS攻击实战与示例代码

XSS, 跨站脚本攻击, 简单来说, 就是非本站点的脚本被执行了。 关于XSS 的详细介绍和防御参考: XSS(跨站脚本)攻击与预防 和 跨站脚本攻击(XSS)及防范措施。 本篇介绍在Java 项目中如何快速修复XSS 漏洞。本篇使用的是黑名单的方式, 对于非法字符进行转义。 黑名单的方式…

Win10的IE以及其他浏览器无法使用的解决方法

前言 最近&#xff0c;突然无法使用Win10的IE&#xff0c;网络连接正常&#xff0c;重置过IE&#xff0c;恢复过高级选项设置&#xff0c;检查过无代理设置&#xff0c;清理过缓存删除IE个人设置&#xff0c;以及清理过DNS解析缓存&#xff0c;IE都会显示无法访问此页面的网络…

Django3还可以这样学之Django的创建

Django (Flask说&#xff1a;“其实我是你的双胞胎哥哥啊”) 一、前言 传统的web框架为MVC设计模式&#xff0c;即M是指业务模型&#xff0c;V是指用户界面&#xff0c;C则是控制器。虽然这种设计模式耦合性低、可重用性高、部署快等特点&#xff0c; 但是完全理解该设计模式…

【机器学习】贝叶斯算法详解 + 公式推导 + 垃圾邮件过滤实战 + Python代码实现

文章目录一、贝叶斯简介二、贝叶斯公式推导三、拼写纠正案例四、垃圾邮件过滤案例4.1 问题描述4.2 朴素贝叶斯引入五、基于朴素贝叶斯的垃圾邮件过滤实战5.1 导入相关库5.2 邮件数据读取5.3 构建语料表&#xff08;字典&#xff09;5.4 构建训练集的特征向量5.5 朴素贝叶斯算法…

web服务器的相关配置

一&#xff1a;简单介绍web服务 1、什么是www www是world wide web的缩写&#xff0c;也就是全球信息广播的意思。通常说的上网就是使用www来查询用户所需要的信息。www可以结合文字、图形、影像以及声音等多媒体&#xff0c;并通过可以让鼠标单击超链接的方式将信息以Interne…

数字化时代,企业如何培养员工的数据思维意识

很多的企业&#xff0c;他们都有意识想通过大数据或者商业智能BI来推动一些数据信息化的项目建设&#xff0c;比如企业的财务分析、经营管理分析、供应链分析、运营、市场分析等等&#xff0c;这都是很好的表现。 但在实际跟进和深入沟通的时候就发现很多分析的想法是没有办法…

OpenCV(9)-图像轮廓

图像轮廓 图像轮廓概述 图像轮廓是具有相同颜色或强度的连续点的曲线 作用&#xff1a; 可以用于图形分析物体的识别与检测 注意&#xff1a; 为了检测的准确性&#xff0c;需要先对图像进行二值化或Canny操作画轮廓时会修改输入的图像 轮廓查找API&#xff1a; findConto…