1/* 2 * Copyright (c) 2024-2025 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 16import { CallGraph, CallGraphNode, CallGraphNodeKind, CallSite, DynCallSite, FuncID, ICallSite } from '../model/CallGraph'; 17import { Scene } from '../../Scene'; 18import { ArkAssignStmt, ArkInvokeStmt, ArkReturnStmt, Stmt } from '../../core/base/Stmt'; 19import { 20 AbstractExpr, 21 AbstractInvokeExpr, 22 ArkInstanceInvokeExpr, 23 ArkNewArrayExpr, 24 ArkNewExpr, 25 ArkPtrInvokeExpr, 26 ArkStaticInvokeExpr, 27} from '../../core/base/Expr'; 28import { AbstractFieldRef, ArkArrayRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef } from '../../core/base/Ref'; 29import { Value } from '../../core/base/Value'; 30import { ArkMethod } from '../../core/model/ArkMethod'; 31import Logger, { LOG_MODULE_TYPE } from '../../utils/logger'; 32import { Local } from '../../core/base/Local'; 33import { NodeID } from '../../core/graph/BaseExplicitGraph'; 34import { ClassSignature } from '../../core/model/ArkSignature'; 35import { ArkClass } from '../../core/model/ArkClass'; 36import { ArrayType, ClassType, FunctionType, StringType } from '../../core/base/Type'; 37import { Constant, NullConstant } from '../../core/base/Constant'; 38import { PAGStat } from '../common/Statistics'; 39import { ContextID, DUMMY_CID, KLimitedContextSensitive } from './Context'; 40import { 41 FuncPag, 42 InterFuncPag, 43 InterProceduralEdge, 44 IntraProceduralEdge, 45 Pag, 46 PagEdgeKind, 47 PagFuncNode, 48 PagGlobalThisNode, 49 PagLocalNode, 50 PagNewContainerExprNode, 51 PagNode, 52 PagNodeType, 53 PagThisRefNode, 54 StorageLinkEdgeType, 55 StorageType, 56} from './Pag'; 57import { GLOBAL_THIS_NAME } from '../../core/common/TSConst'; 58import { IPtsCollection } from './PtsDS'; 59import { UNKNOWN_FILE_NAME } from '../../core/common/Const'; 60import { BuiltApiType, getBuiltInApiType } from './PTAUtils'; 61import { PointerAnalysisConfig, PtaAnalysisScale } from './PointerAnalysisConfig'; 62 63const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'PTA'); 64 65export class CSFuncID { 66 public cid: ContextID; 67 public funcID: FuncID; 68 69 constructor(cid: ContextID, fid: FuncID) { 70 this.cid = cid; 71 this.funcID = fid; 72 } 73} 74 75export class PagBuilder { 76 private pag: Pag; 77 private cg: CallGraph; 78 private scale: PtaAnalysisScale; 79 private funcPags: Map<FuncID, FuncPag>; 80 private interFuncPags?: Map<FuncID, InterFuncPag>; 81 private handledFunc: Set<string> = new Set(); 82 private ctx: KLimitedContextSensitive; 83 private scene: Scene; 84 private worklist: CSFuncID[] = []; 85 private pagStat: PAGStat; 86 // TODO: change string to hash value 87 private staticField2UniqInstanceMap: Map<string, Value> = new Map(); 88 private instanceField2UniqInstanceMap: Map<string, Value> = new Map(); 89 private cid2ThisRefPtMap: Map<ContextID, NodeID> = new Map(); 90 private cid2ThisRefMap: Map<ContextID, NodeID> = new Map(); 91 private cid2ThisLocalMap: Map<ContextID, NodeID> = new Map(); 92 private sdkMethodReturnValueMap: Map<ArkMethod, Map<ContextID, ArkNewExpr>> = new Map(); 93 // record the SDK API param, and create fake Values 94 private methodParamValueMap: Map<FuncID, Map<number, Value>> = new Map(); 95 private fakeSdkMethodParamDeclaringStmt: Stmt = new ArkAssignStmt(new Local(''), new Local('')); 96 private funcHandledThisRound: Set<FuncID> = new Set(); 97 private updatedNodesThisRound: Map<NodeID, IPtsCollection<NodeID>> = new Map(); 98 private singletonFuncMap: Map<FuncID, boolean> = new Map(); 99 private globalThisValue: Local = new Local(GLOBAL_THIS_NAME); 100 private globalThisPagNode?: PagGlobalThisNode; 101 private storagePropertyMap: Map<StorageType, Map<string, Local>> = new Map(); 102 private externalScopeVariableMap: Map<Local, Local[]> = new Map(); 103 private retriggerNodesList: Set<NodeID> = new Set(); 104 105 constructor(p: Pag, cg: CallGraph, s: Scene, kLimit: number, scale: PtaAnalysisScale) { 106 this.pag = p; 107 this.cg = cg; 108 this.scale = scale; 109 this.funcPags = new Map<FuncID, FuncPag>(); 110 this.ctx = new KLimitedContextSensitive(kLimit); 111 this.scene = s; 112 this.pagStat = new PAGStat(); 113 } 114 115 private buildFuncPagAndAddToWorklist(cs: CSFuncID): CSFuncID { 116 if (this.worklist.includes(cs)) { 117 return cs; 118 } 119 120 this.buildFuncPag(cs.funcID); 121 if (this.isSingletonFunction(cs.funcID)) { 122 cs.cid = DUMMY_CID; 123 } 124 125 this.worklist.push(cs); 126 return cs; 127 } 128 129 private addToFuncHandledListThisRound(id: FuncID): void { 130 if (this.funcHandledThisRound.has(id)) { 131 return; 132 } 133 134 this.funcHandledThisRound.add(id); 135 } 136 137 public buildForEntries(funcIDs: FuncID[]): void { 138 this.worklist = []; 139 funcIDs.forEach(funcID => { 140 let cid = this.ctx.getNewContextID(funcID); 141 let csFuncID = new CSFuncID(cid, funcID); 142 this.buildFuncPagAndAddToWorklist(csFuncID); 143 }); 144 145 this.handleReachable(); 146 this.globalThisPagNode = this.getOrNewGlobalThisNode(-1) as PagGlobalThisNode; 147 this.pag.addPagEdge(this.globalThisPagNode, this.globalThisPagNode, PagEdgeKind.Copy); 148 } 149 150 public handleReachable(): boolean { 151 if (this.worklist.length === 0) { 152 return false; 153 } 154 this.funcHandledThisRound.clear(); 155 156 while (this.worklist.length > 0) { 157 let csFunc = this.worklist.shift() as CSFuncID; 158 this.buildPagFromFuncPag(csFunc.funcID, csFunc.cid); 159 this.addToFuncHandledListThisRound(csFunc.funcID); 160 } 161 162 return true; 163 } 164 165 public build(): void { 166 for (let funcID of this.cg.getEntries()) { 167 let cid = this.ctx.getNewContextID(funcID); 168 let csFuncID = new CSFuncID(cid, funcID); 169 this.buildFuncPagAndAddToWorklist(csFuncID); 170 171 this.handleReachable(); 172 } 173 } 174 175 public buildFuncPag(funcID: FuncID): boolean { 176 if (this.funcPags.has(funcID)) { 177 return false; 178 } 179 180 let arkMethod = this.cg.getArkMethodByFuncID(funcID); 181 if (arkMethod == null) { 182 return false; 183 } 184 185 let cfg = arkMethod.getCfg(); 186 if (!cfg) { 187 this.buildSDKFuncPag(funcID); 188 return false; 189 } 190 191 logger.trace(`[build FuncPag] ${arkMethod.getSignature().toString()}`); 192 193 let fpag = new FuncPag(); 194 for (let stmt of cfg.getStmts()) { 195 if (stmt instanceof ArkAssignStmt) { 196 this.processExternalScopeValue(stmt.getRightOp(), funcID); 197 // Add non-call edges 198 let kind = this.getEdgeKindForAssignStmt(stmt); 199 if (kind !== PagEdgeKind.Unknown) { 200 fpag.addInternalEdge(stmt, kind); 201 continue; 202 } 203 204 // handle call 205 this.buildInvokeExprInAssignStmt(stmt, fpag); 206 } else if (stmt instanceof ArkInvokeStmt && this.scale === PtaAnalysisScale.WholeProgram) { 207 this.processExternalScopeValue(stmt.getInvokeExpr(), funcID); 208 this.buildInvokeExprInInvokeStmt(stmt, fpag); 209 } else { 210 // TODO: need handle other type of stmt? 211 } 212 } 213 214 this.funcPags.set(funcID, fpag); 215 this.pagStat.numTotalFunction++; 216 return true; 217 } 218 219 private buildInvokeExprInAssignStmt(stmt: ArkAssignStmt, fpag: FuncPag): void { 220 let ivkExpr = stmt.getInvokeExpr()!; 221 if (ivkExpr instanceof ArkStaticInvokeExpr) { 222 let callSites = this.cg.getCallSiteByStmt(stmt); 223 if (callSites.length !== 0) { 224 // direct call is already existing in CG 225 // TODO: API Invoke stmt has anonymous method param, how to add these param into callee 226 callSites.forEach(cs => { 227 this.addFuncPagCallSite(fpag, cs, ivkExpr); 228 }); 229 } else { 230 throw new Error('Can not find static callsite'); 231 } 232 } else if (ivkExpr instanceof ArkInstanceInvokeExpr || ivkExpr instanceof ArkPtrInvokeExpr) { 233 let ptcs = this.cg.getDynCallsiteByStmt(stmt); 234 if (ptcs) { 235 this.addToDynamicCallSite(fpag, ptcs); 236 } 237 } 238 } 239 240 private addFuncPagCallSite(fpag: FuncPag, cs: CallSite, ivkExpr: AbstractInvokeExpr): void { 241 if (ivkExpr instanceof ArkStaticInvokeExpr) { 242 if (ivkExpr.getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) { 243 fpag.addUnknownCallSite(cs); 244 } else { 245 fpag.addNormalCallSite(cs); 246 } 247 } 248 } 249 250 private buildInvokeExprInInvokeStmt(stmt: ArkInvokeStmt, fpag: FuncPag): void { 251 // TODO: discuss if we need a invokeStmt 252 253 let callSites = this.cg.getCallSiteByStmt(stmt); 254 if (callSites.length !== 0) { 255 // direct call or constructor call is already existing in CG 256 // TODO: some ptr invoke stmt is recognized as Static invoke in tests/resources/callgraph/funPtrTest1/fnPtrTest4.ts 257 // TODO: instance invoke(ptr invoke) 258 callSites.forEach(cs => { 259 if (this.cg.isUnknownMethod(cs.calleeFuncID)) { 260 fpag.addUnknownCallSite(cs); 261 } else { 262 fpag.addNormalCallSite(cs); 263 } 264 }); 265 266 return; 267 } 268 269 let dycs = this.cg.getDynCallsiteByStmt(stmt); 270 if (dycs) { 271 this.addToDynamicCallSite(fpag, dycs); 272 } else { 273 throw new Error('Can not find callsite by stmt'); 274 } 275 } 276 277 private processExternalScopeValue(value: Value, funcID: FuncID): void { 278 let dummyMainFuncID = this.cg.getDummyMainFuncID(); 279 if (dummyMainFuncID && funcID === dummyMainFuncID) { 280 return; 281 } 282 283 if (value instanceof Local) { 284 this.handleValueFromExternalScope(value, funcID); 285 } else if (value instanceof ArkInstanceInvokeExpr) { 286 value.getUses().forEach(v => { 287 this.handleValueFromExternalScope(v, funcID); 288 }); 289 } 290 } 291 292 /** 293 * will not create real funcPag, only create param values 294 */ 295 private buildSDKFuncPag(funcID: FuncID): void { 296 // check if SDK method 297 let cgNode = this.cg.getNode(funcID) as CallGraphNode; 298 if (!cgNode.isSdkMethod()) { 299 return; 300 } 301 let paramArr: Map<number, Value> = this.createDummyParamValue(funcID); 302 303 this.methodParamValueMap.set(funcID, paramArr); 304 } 305 306 private createDummyParamValue(funcID: FuncID, type: number = 1): Map<number, Value> { 307 let arkMethod = this.cg.getArkMethodByFuncID(funcID); 308 if (!arkMethod) { 309 return new Map(); 310 } 311 312 let args = arkMethod.getParameters(); 313 if (!args) { 314 return new Map(); 315 } 316 317 let paramArr: Map<number, Value> = new Map(); 318 319 if (type === 0) { 320 // heapObj 321 args.forEach((arg, index) => { 322 let paramType = arg.getType(); 323 if (!(paramType instanceof ClassType)) { 324 return; 325 // TODO: support more type 326 } 327 328 let argInstance: ArkNewExpr = new ArkNewExpr(paramType); 329 paramArr.set(index, argInstance); 330 }); 331 } else if (type === 1) { 332 // Local 333 args.forEach((arg, index) => { 334 let argInstance: Local = new Local(arg.getName(), arg.getType()); 335 argInstance.setDeclaringStmt(this.fakeSdkMethodParamDeclaringStmt); 336 paramArr.set(index, argInstance); 337 }); 338 } 339 340 return paramArr; 341 } 342 343 private createDummyParamPagNodes(value: Map<number, Value>, funcID: FuncID): Map<number, NodeID> { 344 let paramPagNodes: Map<number, NodeID> = new Map(); 345 let method = this.cg.getArkMethodByFuncID(funcID)!; 346 if (!method || !method.getCfg()) { 347 return paramPagNodes; 348 } 349 350 value.forEach((v, index) => { 351 let paramArkExprNode = this.pag.getOrNewNode(DUMMY_CID, v, this.fakeSdkMethodParamDeclaringStmt); 352 paramPagNodes.set(index, paramArkExprNode.getID()); 353 }); 354 355 return paramPagNodes; 356 } 357 358 public buildPagFromFuncPag(funcID: FuncID, cid: ContextID): void { 359 let funcPag = this.funcPags.get(funcID); 360 if (funcPag === undefined) { 361 return; 362 } 363 if (this.handledFunc.has(`${cid}-${funcID}`)) { 364 return; 365 } 366 367 this.addEdgesFromFuncPag(funcPag, cid, funcID); 368 let interFuncPag = this.interFuncPags?.get(funcID); 369 if (interFuncPag) { 370 this.addEdgesFromInterFuncPag(interFuncPag, cid); 371 } 372 373 this.addCallsEdgesFromFuncPag(funcPag, cid); 374 this.addDynamicCallSite(funcPag, funcID, cid); 375 this.addUnknownCallSite(funcPag, funcID); 376 this.handledFunc.add(`${cid}-${funcID}`); 377 } 378 379 /// Add Pag Nodes and Edges in function 380 public addEdgesFromFuncPag(funcPag: FuncPag, cid: ContextID, funcID: FuncID): boolean { 381 let inEdges = funcPag.getInternalEdges(); 382 if (inEdges === undefined) { 383 return false; 384 } 385 let paramNodes; 386 let paramRefIndex = 0; 387 if (this.scale === PtaAnalysisScale.MethodLevel) { 388 paramNodes = this.createDummyParamPagNodes(this.createDummyParamValue(funcID, 0), funcID); 389 } 390 391 for (let e of inEdges) { 392 let srcPagNode = this.getOrNewPagNode(cid, e.src, e.stmt); 393 let dstPagNode = this.getOrNewPagNode(cid, e.dst, e.stmt); 394 395 this.pag.addPagEdge(srcPagNode, dstPagNode, e.kind, e.stmt); 396 397 // Take place of the real stmt for return 398 if (dstPagNode.getStmt() instanceof ArkReturnStmt) { 399 dstPagNode.setStmt(e.stmt); 400 } 401 402 // for demand-driven analysis, add fake parameter heapObj nodes 403 if (e.src instanceof ArkParameterRef && this.scale === PtaAnalysisScale.MethodLevel) { 404 let paramObjNodeID = paramNodes?.get(paramRefIndex++); 405 if (!paramObjNodeID) { 406 continue; 407 } 408 409 this.pag.addPagEdge(this.pag.getNode(paramObjNodeID) as PagNode, srcPagNode, PagEdgeKind.Address); 410 } 411 } 412 413 return true; 414 } 415 416 /// add Copy edges interprocedural 417 public addCallsEdgesFromFuncPag(funcPag: FuncPag, cid: ContextID): boolean { 418 for (let cs of funcPag.getNormalCallSites()) { 419 let ivkExpr = cs.callStmt.getInvokeExpr(); 420 let calleeCid = this.ctx.getOrNewContext(cid, cs.calleeFuncID, true); 421 422 let calleeCGNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode; 423 424 if (this.scale === PtaAnalysisScale.MethodLevel) { 425 this.addStaticPagCallReturnEdge(cs, cid, calleeCid); 426 } 427 428 // process the Storage API(Static) 429 if (!this.processStorage(cs, calleeCGNode, cid)) { 430 // If not Storage API, process normal edge 431 this.addStaticPagCallEdge(cs, cid, calleeCid); 432 } 433 434 // Add edge to thisRef for special calls 435 if (calleeCGNode.getKind() === CallGraphNodeKind.constructor || calleeCGNode.getKind() === CallGraphNodeKind.intrinsic) { 436 let callee = this.scene.getMethod(this.cg.getMethodByFuncID(cs.calleeFuncID)!)!; 437 if (ivkExpr instanceof ArkInstanceInvokeExpr) { 438 let baseNode = this.getOrNewPagNode(cid, ivkExpr.getBase()); 439 let baseNodeID = baseNode.getID(); 440 441 this.addThisRefCallEdge(baseNodeID, cid, ivkExpr.getBase(), callee, calleeCid, cs.callerFuncID); 442 } else { 443 logger.error(`constructor or intrinsic func is static ${ivkExpr!.toString()}`); 444 } 445 } 446 } 447 448 return true; 449 } 450 451 public addDynamicCallSite(funcPag: FuncPag, funcID: FuncID, cid: ContextID): void { 452 // add dyn callsite in funcpag to base node 453 for (let cs of funcPag.getDynamicCallSites()) { 454 let invokeExpr: AbstractInvokeExpr = cs.callStmt.getInvokeExpr()!; 455 let base!: Local; 456 if (invokeExpr instanceof ArkInstanceInvokeExpr) { 457 base = invokeExpr.getBase(); 458 } else if (invokeExpr instanceof ArkPtrInvokeExpr && invokeExpr.getFuncPtrLocal() instanceof Local) { 459 base = invokeExpr.getFuncPtrLocal() as Local; 460 } else if (invokeExpr instanceof ArkPtrInvokeExpr && invokeExpr.getFuncPtrLocal() instanceof AbstractFieldRef) { 461 /** 462 * TODO: wait for IR change 463 * throw error in ptrInvoke with field ref 464 * this.field() // field is lambda expression 465 */ 466 continue; 467 } 468 // TODO: check base under different cid 469 let baseNodeIDs = this.pag.getNodesByValue(base); 470 if (!baseNodeIDs) { 471 // bind the call site to export base 472 let interProceduralLocal = this.getSourceValueFromExternalScope(base, funcID); 473 if (interProceduralLocal) { 474 baseNodeIDs = this.pag.getNodesByValue(interProceduralLocal); 475 } 476 } 477 478 if (!baseNodeIDs) { 479 logger.warn(`[build dynamic call site] can not handle call site with base ${base.toString()}`); 480 continue; 481 } 482 483 for (let nodeID of baseNodeIDs!.values()) { 484 let node = this.pag.getNode(nodeID); 485 if (!(node instanceof PagLocalNode)) { 486 continue; 487 } 488 489 node.addRelatedDynCallSite(cs); 490 } 491 492 if (cs.callStmt instanceof ArkAssignStmt) { 493 this.getOrNewPagNode(cid, cs.callStmt.getLeftOp(), cs.callStmt); 494 } 495 } 496 } 497 498 public addUnknownCallSite(funcPag: FuncPag, funcID: FuncID): void { 499 let method = this.cg.getArkMethodByFuncID(funcID); 500 501 if (!method) { 502 throw new Error(`can not find ArkMethod by FuncID ${funcID}`); 503 } 504 505 let locals = method.getBody()?.getLocals()!; 506 507 funcPag.getUnknownCallSites().forEach(unknownCallSite => { 508 let calleeName = unknownCallSite.callStmt.getInvokeExpr()?.getMethodSignature().getMethodSubSignature().getMethodName()!; 509 510 let base = locals.get(calleeName); 511 if (!base) { 512 return; 513 } 514 let baseNodeIDs = this.pag.getNodesByValue(base); 515 if (!baseNodeIDs) { 516 logger.warn(`[build dynamic call site] can not handle call site with base ${base.toString()}`); 517 return; 518 } 519 for (let nodeID of baseNodeIDs!.values()) { 520 let node = this.pag.getNode(nodeID); 521 if (!(node instanceof PagLocalNode)) { 522 continue; 523 } 524 525 node.addRelatedUnknownCallSite(unknownCallSite); 526 } 527 }); 528 } 529 530 public addDynamicCallEdge(cs: ICallSite, baseClassPTNode: NodeID, cid: ContextID): NodeID[] { 531 let srcNodes: NodeID[] = []; 532 let ivkExpr = cs.callStmt.getInvokeExpr(); 533 534 let ptNode = this.pag.getNode(baseClassPTNode) as PagNode; 535 let value = (ptNode as PagNode).getValue(); 536 let callees: ArkMethod[] = this.getDynamicCallee(ptNode, value, ivkExpr!, cs); 537 538 for (let callee of callees) { 539 if (!callee) { 540 continue; 541 } 542 // get caller and callee CG node, add param and return value PAG edge 543 let dstCGNode = this.cg.getCallGraphNodeByMethod(callee.getSignature()); 544 let callerNode = this.cg.getNode(cs.callerFuncID) as CallGraphNode; 545 if (!callerNode) { 546 throw new Error('Can not get caller method node'); 547 } 548 // update call graph 549 // TODO: movo to cgbuilder 550 551 this.cg.addDynamicCallEdge(callerNode.getID(), dstCGNode.getID(), cs.callStmt); 552 553 if (this.cg.detectReachable(dstCGNode.getID(), callerNode.getID())) { 554 return srcNodes; 555 } 556 557 let calleeCid = this.ctx.getOrNewContext(cid, dstCGNode.getID(), true); 558 let staticCS = new CallSite(cs.callStmt, cs.args, dstCGNode.getID(), cs.callerFuncID); 559 560 if (this.scale === PtaAnalysisScale.MethodLevel) { 561 srcNodes.push(...this.addStaticPagCallReturnEdge(staticCS, baseClassPTNode, calleeCid)); 562 continue; 563 } 564 565 if (getBuiltInApiType(ivkExpr?.getMethodSignature()!) === BuiltApiType.NotBuiltIn) { 566 srcNodes.push(...this.processNormalMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode)); 567 } else { 568 // special SDK call: Container API, Function API 569 srcNodes.push(...this.processBuiltInMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode)); 570 } 571 } 572 573 return srcNodes; 574 } 575 576 /** 577 * all possible callee methods of a dynamic call site 578 * handle both PtrInvokeExpr and InstanceInvokeExpr 579 */ 580 private getDynamicCallee(ptNode: PagNode, value: Value, ivkExpr: AbstractInvokeExpr, cs: ICallSite): ArkMethod[] { 581 let callee: ArkMethod[] = []; 582 583 if (ptNode instanceof PagFuncNode) { 584 // function ptr invoke 585 let tempCallee = this.scene.getMethod(ptNode.getMethod()); 586 587 if (!callee) { 588 return callee; 589 } 590 callee.push(tempCallee!); 591 return callee; 592 } 593 //else branch 594 let calleeName = ivkExpr!.getMethodSignature().getMethodSubSignature().getMethodName(); 595 // instance method invoke 596 if (!(value instanceof ArkNewExpr || value instanceof ArkNewArrayExpr)) { 597 return callee; 598 } 599 let tempCallee; 600 601 // try to get callee by MethodSignature 602 if (value instanceof ArkNewExpr) { 603 // get class signature 604 let clsSig = (value.getType() as ClassType).getClassSignature() as ClassSignature; 605 let cls; 606 607 cls = this.scene.getClass(clsSig) as ArkClass; 608 609 while (!tempCallee && cls) { 610 tempCallee = cls.getMethodWithName(calleeName); 611 cls = cls.getSuperClass(); 612 } 613 614 if (!tempCallee) { 615 tempCallee = this.scene.getMethod(ivkExpr!.getMethodSignature()); 616 } 617 } 618 619 if (!tempCallee && cs.args) { 620 // while pts has {o_1, o_2} and invoke expr represents a method that only {o_1} has 621 // return empty node when {o_2} come in 622 // try to get callee by anonymous method in param 623 for (let arg of cs.args) { 624 // TODO: anonymous method param and return value pointer pass 625 let argType = arg.getType(); 626 if (argType instanceof FunctionType) { 627 callee.push(this.scene.getMethod(argType.getMethodSignature())!); 628 } 629 } 630 } else if (tempCallee) { 631 callee.push(tempCallee); 632 } 633 634 return callee; 635 } 636 637 public processNormalMethodPagCallEdge(staticCS: CallSite, cid: ContextID, calleeCid: ContextID, baseClassPTNode: NodeID): NodeID[] { 638 let srcNodes: NodeID[] = []; 639 let ivkExpr = staticCS.callStmt.getInvokeExpr()!; 640 let ptNode = this.pag.getNode(baseClassPTNode) as PagNode; 641 let dstCGNode = this.cg.getNode(staticCS.calleeFuncID) as CallGraphNode; 642 let callee = this.cg.getArkMethodByFuncID(staticCS.calleeFuncID); 643 // Dynamic call, Ptr call, normal SDK call 644 srcNodes.push(...this.addStaticPagCallEdge(staticCS, cid, calleeCid, ptNode)); 645 646 // Pass base's pts to callee's this pointer 647 if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkInstanceInvokeExpr) { 648 let srcBaseNode = this.addThisRefCallEdge(baseClassPTNode, cid, ivkExpr.getBase(), callee!, calleeCid, staticCS.callerFuncID); 649 650 if (srcBaseNode !== -1) { 651 srcNodes.push(srcBaseNode); 652 } 653 } else if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkPtrInvokeExpr) { 654 let originCS = (ptNode as PagFuncNode).getCS(); 655 if (!originCS) { 656 return srcNodes; 657 } 658 659 let thisValue = originCS.args![0]; 660 661 if (!(thisValue instanceof Local)) { 662 return srcNodes; 663 } 664 this.addThisRefCallEdge(baseClassPTNode, (ptNode as PagFuncNode).getOriginCid(), thisValue, callee!, calleeCid, staticCS.callerFuncID); 665 } 666 667 return srcNodes; 668 } 669 670 /** 671 * include container API, Function API 672 */ 673 public processBuiltInMethodPagCallEdge(staticCS: CallSite, cid: ContextID, calleeCid: ContextID, baseClassPTNode: NodeID): NodeID[] { 674 let srcNodes: NodeID[] = []; 675 let ivkExpr = staticCS.callStmt.getInvokeExpr()!; 676 let callee = this.scene.getMethod(ivkExpr.getMethodSignature()); 677 let realCallee = this.cg.getArkMethodByFuncID(staticCS.calleeFuncID); 678 if (!callee) { 679 return srcNodes; 680 } 681 682 let builtInType = getBuiltInApiType(callee.getSignature()); 683 684 if (builtInType === BuiltApiType.NotBuiltIn || !realCallee) { 685 return srcNodes; 686 } 687 688 switch (builtInType) { 689 case BuiltApiType.SetAdd: 690 case BuiltApiType.MapSet: 691 this.processContainerPagCallEdge(staticCS, cid, baseClassPTNode, builtInType); 692 break; 693 694 case BuiltApiType.FunctionCall: 695 /** 696 * set this and param 697 * function.call(thisArg, arg1, arg2, ...) 698 */ 699 this.handleFunctionCall(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode); 700 break; 701 702 case BuiltApiType.FunctionApply: 703 /** 704 * set this, resolve array param 705 * function.apply(thisArg, [argsArray]) 706 */ 707 this.handleFunctionApply(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode); 708 break; 709 710 case BuiltApiType.FunctionBind: 711 /** 712 * clone the function node and add the this pointer, origin callsite, args offset to it 713 * let f = function.bind(thisArg, arg1, arg2, ...) 714 * f(); 715 */ 716 this.handleFunctionBind(staticCS, cid, baseClassPTNode, srcNodes); 717 break; 718 default: 719 } 720 721 return srcNodes; 722 } 723 724 private processContainerPagCallEdge(cs: CallSite, cid: ContextID, baseClassPTNode: NodeID, type: BuiltApiType): NodeID[] { 725 let srcNodes: NodeID[] = []; 726 let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode; 727 let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod()); 728 let ptNode = this.pag.getNode(baseClassPTNode) as PagNode; 729 730 if (!calleeMethod || !(ptNode instanceof PagNewContainerExprNode)) { 731 return srcNodes; 732 } 733 734 let containerValue = (cs.callStmt.getInvokeExpr() as ArkInstanceInvokeExpr).getBase(); 735 736 const containerValueProcess = (argIndex: number): void => { 737 let srcNode = this.pag.getOrNewNode(cid, cs.args![argIndex], cs.callStmt); 738 let realContainerFieldPagNode = this.pag.getOrClonePagContainerFieldNode(baseClassPTNode, undefined, containerValue); 739 740 if (realContainerFieldPagNode) { 741 // In some cases, the value of a variable of array type may not be an explicit array object, 742 // and the value of `realContainerFieldPagNode` will be undefined. 743 this.pag.addPagEdge(srcNode, realContainerFieldPagNode, PagEdgeKind.Copy, cs.callStmt); 744 srcNodes.push(srcNode.getID()); 745 } 746 }; 747 748 if (type === BuiltApiType.SetAdd) { 749 containerValueProcess(0); 750 } else if (type === BuiltApiType.MapSet) { 751 containerValueProcess(1); 752 } 753 754 return srcNodes; 755 } 756 757 private handleFunctionCall( 758 staticCS: CallSite, 759 cid: ContextID, 760 calleeCid: ContextID, 761 realCallee: ArkMethod, 762 srcNodes: NodeID[], 763 baseClassPTNode: NodeID 764 ): void { 765 this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, staticCS.calleeFuncID)); 766 srcNodes.push(...this.addCallParamPagEdge(realCallee, staticCS.args!, staticCS.callStmt, cid, calleeCid, 1)); 767 this.addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid); 768 } 769 770 private handleFunctionApply( 771 staticCS: CallSite, 772 cid: ContextID, 773 calleeCid: ContextID, 774 realCallee: ArkMethod, 775 srcNodes: NodeID[], 776 baseClassPTNode: NodeID 777 ): void { 778 this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, staticCS.calleeFuncID)); 779 let callerMethod = this.cg.getArkMethodByFuncID(staticCS.callerFuncID); 780 if (!callerMethod) { 781 throw new Error('Cannot get caller method'); 782 } 783 let argsRealValues = this.transferArrayValues(callerMethod, staticCS.args![1]); 784 srcNodes.push(...this.addCallParamPagEdge(realCallee, argsRealValues, staticCS.callStmt, cid, calleeCid, 0)); 785 this.addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid); 786 } 787 788 private handleFunctionBind(staticCS: CallSite, cid: ContextID, baseClassPTNode: NodeID, srcNodes: NodeID[]): void { 789 let srcNode = this.pag.getOrClonePagFuncNode(baseClassPTNode); 790 if (!srcNode) { 791 return; 792 } 793 this.setFunctionThisPt(staticCS, srcNode, cid); 794 795 let dstNode = this.getOrNewPagNode(cid, (staticCS.callStmt as ArkAssignStmt).getLeftOp() as Local); 796 this.pag.addPagEdge(srcNode, dstNode, PagEdgeKind.Copy, staticCS.callStmt); 797 srcNodes.push(srcNode.getID()); 798 799 srcNode.setCS(staticCS); 800 srcNode.setArgsOffset(1); 801 srcNode.setOriginCid(cid); 802 } 803 804 private addThisEdge(staticCS: CallSite, cid: ContextID, realCallee: ArkMethod, srcNodes: NodeID[], baseClassPTNode: NodeID, calleeCid: ContextID): void { 805 if (!(staticCS.args![0] instanceof NullConstant) && !realCallee.isStatic()) { 806 let srcNodeID = this.addThisRefCallEdge(baseClassPTNode, cid, staticCS.args![0] as Local, realCallee, calleeCid, staticCS.callerFuncID); 807 808 if (srcNodeID !== -1) { 809 srcNodes.push(srcNodeID); 810 } 811 } 812 } 813 814 private setFunctionThisPt(staticCS: CallSite, srcNode: PagFuncNode, cid: ContextID): void { 815 let thisLocal = staticCS.args![0]; 816 if (!(thisLocal instanceof Local)) { 817 return; 818 } 819 820 let thisInstanceLocal = this.getRealThisLocal(thisLocal, staticCS.callerFuncID); 821 let baseThisNode = this.pag.getOrNewNode(cid, thisInstanceLocal); 822 823 for (let pt of baseThisNode.getPointTo()) { 824 srcNode.setThisPt(pt); 825 } 826 } 827 828 public handleUnkownDynamicCall(cs: DynCallSite, cid: ContextID): NodeID[] { 829 let srcNodes: NodeID[] = []; 830 let callerNode = this.cg.getNode(cs.callerFuncID) as CallGraphNode; 831 let ivkExpr = cs.callStmt.getInvokeExpr() as AbstractInvokeExpr; 832 logger.warn('Handling unknown dyn call site : \n ' + callerNode.getMethod().toString() + '\n --> ' + ivkExpr.toString() + '\n CID: ' + cid); 833 834 let callees: ArkMethod[] = []; 835 let callee: ArkMethod | null = null; 836 callee = this.scene.getMethod(ivkExpr.getMethodSignature()); 837 if (!callee) { 838 cs.args?.forEach(arg => { 839 if (!(arg.getType() instanceof FunctionType)) { 840 return; 841 } 842 843 callee = this.scene.getMethod((arg.getType() as FunctionType).getMethodSignature()); 844 if (callee) { 845 callees.push(callee); 846 } 847 }); 848 } else { 849 callees.push(callee); 850 } 851 852 if (callees.length === 0) { 853 return srcNodes; 854 } 855 856 callees.forEach(callee => { 857 let dstCGNode = this.cg.getCallGraphNodeByMethod(callee.getSignature()); 858 if (!callerNode) { 859 throw new Error('Can not get caller method node'); 860 } 861 862 if (this.processStorage(cs, dstCGNode, cid)) { 863 if (ivkExpr.getArgs().length !== 0) { 864 // for AppStorage.set() instance invoke, add obj to reanalyze list 865 let argsNode = this.pag.getOrNewNode(cid, cs.args![0]); 866 srcNodes.push(argsNode.getID()); 867 } 868 } 869 870 logger.warn(`\tAdd call edge of unknown call ${callee.getSignature().toString()}`); 871 this.cg.addDynamicCallEdge(callerNode.getID(), dstCGNode.getID(), cs.callStmt); 872 if (!this.cg.detectReachable(dstCGNode.getID(), callerNode.getID())) { 873 let calleeCid = this.ctx.getOrNewContext(cid, dstCGNode.getID(), true); 874 let staticCS = new CallSite(cs.callStmt, cs.args, dstCGNode.getID(), cs.callerFuncID); 875 let staticSrcNodes = this.addStaticPagCallEdge(staticCS, cid, calleeCid); 876 srcNodes.push(...staticSrcNodes); 877 } 878 }); 879 return srcNodes; 880 } 881 882 public handleUnprocessedCallSites(processedCallSites: Set<DynCallSite>): NodeID[] { 883 let reAnalyzeNodes: NodeID[] = []; 884 for (let funcID of this.funcHandledThisRound) { 885 let funcPag = this.funcPags.get(funcID); 886 if (!funcPag) { 887 logger.error(`can not find funcPag of handled func ${funcID}`); 888 continue; 889 } 890 let callSites = funcPag.getDynamicCallSites(); 891 892 const diffCallSites = new Set(Array.from(callSites).filter(item => !processedCallSites.has(item))); 893 diffCallSites.forEach(cs => { 894 let ivkExpr = cs.callStmt.getInvokeExpr(); 895 if (!(ivkExpr instanceof ArkInstanceInvokeExpr)) { 896 return; 897 } 898 // Get local of base class 899 let base = ivkExpr.getBase(); 900 // TODO: remove this after multiple this local fixed 901 base = this.getRealThisLocal(base, cs.callerFuncID); 902 // Get PAG nodes for this base's local 903 let ctx2NdMap = this.pag.getNodesByValue(base); 904 if (!ctx2NdMap) { 905 return; 906 } 907 908 for (let [cid] of ctx2NdMap.entries()) { 909 reAnalyzeNodes.push(...this.handleUnkownDynamicCall(cs, cid)); 910 } 911 }); 912 } 913 914 return reAnalyzeNodes; 915 } 916 917 private addThisRefCallEdge( 918 baseClassPTNode: NodeID, 919 cid: ContextID, 920 baseLocal: Local, 921 callee: ArkMethod, 922 calleeCid: ContextID, 923 callerFunID: FuncID 924 ): NodeID { 925 let thisRefNodeID = this.recordThisRefNode(baseClassPTNode, callee, calleeCid); 926 if (thisRefNodeID === -1) { 927 return -1; 928 } 929 930 let thisRefNode = this.pag.getNode(thisRefNodeID) as PagThisRefNode; 931 let srcBaseLocal = baseLocal; 932 srcBaseLocal = this.getRealThisLocal(srcBaseLocal, callerFunID); 933 let srcNodeId = this.pag.hasCtxNode(cid, srcBaseLocal); 934 if (!srcNodeId) { 935 // this check is for export local and closure use 936 // replace the invoke base, because its origin base has no pag node 937 let interProceduralLocal = this.getSourceValueFromExternalScope(srcBaseLocal, callerFunID); 938 if (interProceduralLocal) { 939 srcNodeId = this.pag.hasCtxNode(cid, interProceduralLocal); 940 } 941 } 942 943 if (!srcNodeId) { 944 throw new Error('Can not get base node'); 945 } 946 947 this.pag.addPagEdge(this.pag.getNode(srcNodeId) as PagNode, thisRefNode, PagEdgeKind.This); 948 return srcNodeId; 949 } 950 951 private recordThisRefNode(baseClassPTNode: NodeID, callee: ArkMethod, calleeCid: ContextID): NodeID { 952 if (!callee || !callee.getCfg()) { 953 logger.error(`callee is null`); 954 return -1; 955 } 956 let thisAssignStmt = callee 957 .getCfg() 958 ?.getStmts() 959 .filter(s => s instanceof ArkAssignStmt && s.getRightOp() instanceof ArkThisRef); 960 let thisPtr = (thisAssignStmt?.[0] as ArkAssignStmt).getRightOp() as ArkThisRef; 961 if (!thisPtr) { 962 throw new Error('Can not get this ptr'); 963 } 964 965 // IMPORTANT: set cid 2 base Pt info firstly 966 this.cid2ThisRefPtMap.set(calleeCid, baseClassPTNode); 967 let thisRefNode = this.getOrNewThisRefNode(calleeCid, thisPtr) as PagThisRefNode; 968 thisRefNode.addPTNode(baseClassPTNode); 969 970 return thisRefNode.getID(); 971 } 972 973 /* 974 * Add copy edges from arguments to parameters 975 * ret edges from return values to callsite 976 * Return src node 977 */ 978 public addStaticPagCallEdge(cs: CallSite, callerCid: ContextID, calleeCid?: ContextID, ptNode?: PagNode): NodeID[] { 979 if (!calleeCid) { 980 calleeCid = this.ctx.getOrNewContext(callerCid, cs.calleeFuncID, true); 981 } 982 983 let srcNodes: NodeID[] = []; 984 // Add reachable 985 986 let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode; 987 let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod()); 988 if (!calleeMethod) { 989 // TODO: check if nodes need to delete 990 return srcNodes; 991 } 992 if (calleeNode.isSdkMethod()) { 993 srcNodes.push(...this.addSDKMethodPagCallEdge(cs, callerCid, calleeCid)); 994 return srcNodes; 995 } 996 997 if (!calleeMethod.getCfg()) { 998 // method have no cfg body 999 return srcNodes; 1000 } 1001 1002 let calleeCS = this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, cs.calleeFuncID)); 1003 // callee cid will updated if callee is singleton 1004 calleeCid = calleeCS.cid; 1005 1006 let realArgs: Value[] = cs.args ?? []; 1007 let argsOffset: number = 0; 1008 if (ptNode && ptNode instanceof PagFuncNode && ptNode.getCS()) { 1009 // for ptr invoke cloned by Function.bind() 1010 realArgs = ptNode.getCS().args ?? []; 1011 argsOffset = ptNode.getArgsOffset() ?? 0; 1012 callerCid = ptNode.getOriginCid() ?? callerCid; 1013 } 1014 1015 srcNodes.push(...this.addCallParamPagEdge(calleeMethod, realArgs, cs.callStmt, callerCid, calleeCid, argsOffset)); 1016 srcNodes.push(...this.addCallReturnPagEdge(calleeMethod, cs.callStmt, callerCid, calleeCid)); 1017 1018 return srcNodes; 1019 } 1020 1021 /** 1022 * only process the param PAG edge for invoke stmt 1023 */ 1024 private addCallParamPagEdge(calleeMethod: ArkMethod, args: Value[], callStmt: Stmt, callerCid: ContextID, calleeCid: ContextID, offset: number): NodeID[] { 1025 let params = calleeMethod 1026 .getCfg()! 1027 .getStmts() 1028 .filter(stmt => stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkParameterRef) 1029 .map(stmt => (stmt as ArkAssignStmt).getRightOp()); 1030 1031 let srcNodes: NodeID[] = []; 1032 1033 /** 1034 * process foreach situation 1035 * e.g. arr.forEach((item) => { ... }) 1036 * cs.args is anonymous method local, will have only 1 parameter 1037 * but inside foreach will have >= 1 parameters 1038 */ 1039 if (callStmt.getInvokeExpr()?.getMethodSignature().getMethodSubSignature().getMethodName() === 'forEach') { 1040 srcNodes.push(...this.addForeachParamPagEdge(callerCid, calleeCid, callStmt, params)); 1041 return srcNodes; 1042 } 1043 1044 // add args to parameters edges 1045 for (let i = offset; i <= args.length; i++) { 1046 let arg = args[i]; 1047 let param = params[i - offset]; 1048 if (!arg || !param) { 1049 return srcNodes; 1050 } 1051 1052 if (arg instanceof Constant || arg instanceof AbstractExpr) { 1053 // TODO: handle AbstractExpr 1054 continue; 1055 } 1056 1057 // Get or create new PAG node for argument and parameter 1058 let srcPagNode = this.getOrNewPagNode(callerCid, arg, callStmt); 1059 let dstPagNode = this.getOrNewPagNode(calleeCid, param, callStmt); 1060 1061 this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, callStmt); 1062 srcNodes.push(srcPagNode.getID()); 1063 // TODO: handle other types of parmeters 1064 } 1065 1066 return srcNodes; 1067 } 1068 1069 /** 1070 * temporary solution for foreach 1071 * deprecate when foreach is handled by built-in method 1072 * connect the element node with the value inside foreach 1073 */ 1074 private addForeachParamPagEdge(callerCid: ContextID, calleeCid: ContextID, callStmt: Stmt, params: Value[]): NodeID[] { 1075 // container value is the base value of callstmt, its points-to is PagNewContainerExprNode 1076 let srcNodes: NodeID[] = []; 1077 let containerValue = (callStmt.getInvokeExpr() as ArkInstanceInvokeExpr).getBase(); 1078 let param = params[0]; 1079 if (!containerValue || !param) { 1080 return srcNodes; 1081 } 1082 1083 let basePagNode = this.getOrNewPagNode(callerCid, containerValue, callStmt); 1084 let dstPagNode = this.getOrNewPagNode(calleeCid, param, callStmt); 1085 1086 for (let pt of basePagNode.getPointTo()) { 1087 let newContainerExprPagNode = this.pag.getNode(pt) as PagNewContainerExprNode; 1088 1089 // PagNewContainerExprNode's points-to is the element node 1090 if (!newContainerExprPagNode || !newContainerExprPagNode.getElementNode()) { 1091 continue; 1092 } 1093 let srcPagNode = this.pag.getNode(newContainerExprPagNode.getElementNode()!) as PagNode; 1094 1095 // connect the element node with the value inside foreach 1096 this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, callStmt); 1097 srcNodes.push(srcPagNode.getID()); 1098 } 1099 1100 return srcNodes; 1101 } 1102 1103 /** 1104 * process the return value PAG edge for invoke stmt 1105 */ 1106 public addCallReturnPagEdge(calleeMethod: ArkMethod, callStmt: Stmt, callerCid: ContextID, calleeCid: ContextID): NodeID[] { 1107 let srcNodes: NodeID[] = []; 1108 // add ret to caller edges 1109 let retStmts = calleeMethod.getReturnStmt(); 1110 // TODO: call statement must be a assignment state 1111 if (callStmt instanceof ArkAssignStmt) { 1112 let retDst = callStmt.getLeftOp(); 1113 for (let retStmt of retStmts) { 1114 let retValue = (retStmt as ArkReturnStmt).getOp(); 1115 if (retValue instanceof Local) { 1116 let srcPagNode = this.getOrNewPagNode(calleeCid, retValue, retStmt); 1117 let dstPagNode = this.getOrNewPagNode(callerCid, retDst, callStmt); 1118 1119 this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, retStmt); 1120 } else if (retValue instanceof Constant) { 1121 continue; 1122 } else if (retValue instanceof AbstractExpr) { 1123 logger.debug(retValue); 1124 continue; 1125 } else { 1126 throw new Error('return dst not a local or constant, but: ' + retValue.getType().toString()); 1127 } 1128 } 1129 } 1130 1131 return srcNodes; 1132 } 1133 1134 public addStaticPagCallReturnEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID): NodeID[] { 1135 if (!calleeCid) { 1136 calleeCid = this.ctx.getOrNewContext(callerCid, cs.calleeFuncID, true); 1137 } 1138 1139 let srcNodes: NodeID[] = []; 1140 // Add reachable 1141 1142 let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode; 1143 let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod()); 1144 if (!calleeMethod) { 1145 // TODO: check if nodes need to delete 1146 return srcNodes; 1147 } 1148 srcNodes.push(...this.addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod)); 1149 return srcNodes; 1150 } 1151 1152 private addSDKMethodPagCallEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID): NodeID[] { 1153 let srcNodes: NodeID[] = []; 1154 let calleeNode = this.cg.getNode(cs.calleeFuncID) as CallGraphNode; 1155 let calleeMethod: ArkMethod | null = this.scene.getMethod(calleeNode.getMethod()); 1156 1157 if (!calleeMethod) { 1158 return srcNodes; 1159 } 1160 let methodType = getBuiltInApiType(calleeMethod.getSignature()); 1161 1162 // block the container SDK 1163 if (methodType === BuiltApiType.SetAdd || BuiltApiType.MapSet) { 1164 return srcNodes; 1165 } 1166 1167 if (!this.methodParamValueMap.has(calleeNode.getID())) { 1168 this.buildSDKFuncPag(calleeNode.getID()); 1169 } 1170 1171 srcNodes.push(...this.addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod)); 1172 srcNodes.push(...this.addSDKMethodParamPagEdge(cs, callerCid, calleeCid, calleeNode.getID())); 1173 return srcNodes; 1174 } 1175 1176 private addSDKMethodReturnPagEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID, calleeMethod: ArkMethod): NodeID[] { 1177 let srcNodes: NodeID[] = []; 1178 let returnType = calleeMethod.getReturnType(); 1179 if (!(returnType instanceof ClassType) || !(cs.callStmt instanceof ArkAssignStmt)) { 1180 return srcNodes; 1181 } 1182 1183 // check fake heap object exists or not 1184 let cidMap = this.sdkMethodReturnValueMap.get(calleeMethod); 1185 if (!cidMap) { 1186 cidMap = new Map(); 1187 } 1188 let newExpr = cidMap.get(calleeCid); 1189 if (!newExpr) { 1190 if (returnType instanceof ClassType) { 1191 newExpr = new ArkNewExpr(returnType); 1192 } 1193 } 1194 cidMap.set(calleeCid, newExpr!); 1195 this.sdkMethodReturnValueMap.set(calleeMethod, cidMap); 1196 1197 let srcPagNode = this.getOrNewPagNode(calleeCid, newExpr!); 1198 let dstPagNode = this.getOrNewPagNode(callerCid, cs.callStmt.getLeftOp(), cs.callStmt); 1199 1200 this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Address, cs.callStmt); 1201 srcNodes.push(srcPagNode.getID()); 1202 return srcNodes; 1203 } 1204 1205 private addSDKMethodParamPagEdge(cs: CallSite, callerCid: ContextID, calleeCid: ContextID, funcID: FuncID): NodeID[] { 1206 let argNum = cs.args?.length; 1207 let srcNodes: NodeID[] = []; 1208 1209 if (!argNum) { 1210 return srcNodes; 1211 } 1212 1213 // add args to parameters edges 1214 for (let i = 0; i < argNum; i++) { 1215 let arg = cs.args?.[i]; 1216 let paramValue; 1217 1218 if (arg instanceof Local && arg.getType() instanceof FunctionType) { 1219 // TODO: cannot find value 1220 paramValue = this.methodParamValueMap.get(funcID)!.get(i); 1221 } else { 1222 continue; 1223 } 1224 1225 if (!(arg && paramValue)) { 1226 continue; 1227 } 1228 1229 // Get or create new PAG node for argument and parameter 1230 let srcPagNode = this.getOrNewPagNode(callerCid, arg, cs.callStmt); 1231 let dstPagNode = this.getOrNewPagNode(calleeCid, paramValue, cs.callStmt); 1232 1233 if (dstPagNode instanceof PagLocalNode) { 1234 // set the fake param Value in PagLocalNode 1235 /** 1236 * TODO: !!! 1237 * some API param is in the form of anonymous method: 1238 * component/common.d.ts 1239 * declare function animateTo(value: AnimateParam, event: () => void): void; 1240 * 1241 * this param fake Value will create PagFuncNode rather than PagLocalNode 1242 * when this API is called, the anonymous method pointer will not be able to pass into the fake Value PagNode 1243 */ 1244 dstPagNode.setSdkParam(); 1245 let sdkParamInvokeStmt = new ArkInvokeStmt(new ArkPtrInvokeExpr((arg.getType() as FunctionType).getMethodSignature(), paramValue as Local, [])); 1246 1247 // create new DynCallSite 1248 let sdkParamCallSite = new DynCallSite(sdkParamInvokeStmt, undefined, undefined, funcID); 1249 dstPagNode.addRelatedDynCallSite(sdkParamCallSite); 1250 } 1251 1252 this.pag.addPagEdge(srcPagNode, dstPagNode, PagEdgeKind.Copy, cs.callStmt); 1253 srcNodes.push(srcPagNode.getID()); 1254 } 1255 1256 return srcNodes; 1257 } 1258 1259 public getOrNewPagNode(cid: ContextID, v: PagNodeType, s?: Stmt): PagNode { 1260 if (v instanceof ArkThisRef) { 1261 return this.getOrNewThisRefNode(cid, v as ArkThisRef); 1262 } 1263 1264 // this local is also not uniq!!! 1265 // remove below block once this issue fixed 1266 1267 // globalThis process can not be removed while all `globalThis` ref is the same Value 1268 if (v instanceof Local) { 1269 if (v.getName() === 'this') { 1270 return this.getOrNewThisLoalNode(cid, v as Local, s); 1271 } else if (v.getName() === GLOBAL_THIS_NAME && v.getDeclaringStmt() == null) { 1272 // globalThis node has no cid 1273 return this.getOrNewGlobalThisNode(-1); 1274 } 1275 } 1276 1277 if (v instanceof ArkInstanceFieldRef || v instanceof ArkStaticFieldRef) { 1278 v = this.getRealInstanceRef(v); 1279 } 1280 1281 return this.pag.getOrNewNode(cid, v, s); 1282 } 1283 1284 /** 1285 * return ThisRef PAG node according to cid, a cid has a unique ThisRef node 1286 * @param cid: current contextID 1287 */ 1288 public getOrNewThisRefNode(cid: ContextID, v: ArkThisRef): PagNode { 1289 let thisRefNodeID = this.cid2ThisRefMap.get(cid); 1290 if (!thisRefNodeID) { 1291 thisRefNodeID = -1; 1292 } 1293 1294 let thisRefNode = this.pag.getOrNewThisRefNode(thisRefNodeID, v); 1295 this.cid2ThisRefMap.set(cid, thisRefNode.getID()); 1296 return thisRefNode; 1297 } 1298 1299 // TODO: remove it once this local not uniq issue is fixed 1300 public getOrNewThisLoalNode(cid: ContextID, v: Local, s?: Stmt): PagNode { 1301 let thisLocalNodeID = this.cid2ThisLocalMap.get(cid); 1302 if (thisLocalNodeID) { 1303 return this.pag.getNode(thisLocalNodeID) as PagNode; 1304 } 1305 1306 let thisNode = this.pag.getOrNewNode(cid, v, s); 1307 this.cid2ThisLocalMap.set(cid, thisNode.getID()); 1308 return thisNode; 1309 } 1310 1311 public getOrNewGlobalThisNode(cid: ContextID): PagNode { 1312 return this.pag.getOrNewNode(cid, this.getGlobalThisValue()); 1313 } 1314 1315 public getUniqThisLocalNode(cid: ContextID): NodeID | undefined { 1316 return this.cid2ThisLocalMap.get(cid); 1317 } 1318 1319 /** 1320 * search the storage map to get propertyNode with given storage and propertyFieldName 1321 * @param storage storage type: AppStorage, LocalStorage etc. 1322 * @param propertyName string property key 1323 * @returns propertyNode: PagLocalNode 1324 */ 1325 public getOrNewPropertyNode(storage: StorageType, propertyName: string, stmt: Stmt): PagNode { 1326 let propertyNode = this.getPropertyNode(storage, propertyName, stmt); 1327 1328 if (propertyNode) { 1329 return propertyNode; 1330 } 1331 1332 let storageMap = this.storagePropertyMap.get(storage)!; 1333 let propertyLocal = new Local(propertyName); 1334 storageMap.set(propertyName, propertyLocal); 1335 this.storagePropertyMap.set(storage, storageMap); 1336 1337 return this.getOrNewPagNode(-1, propertyLocal, stmt); 1338 } 1339 1340 public getPropertyNode(storage: StorageType, propertyName: string, stmt: Stmt): PagNode | undefined { 1341 let storageMap = this.storagePropertyMap.get(storage); 1342 let propertyLocal: Local | undefined; 1343 1344 if (!storageMap) { 1345 storageMap = new Map(); 1346 this.storagePropertyMap.set(storage, storageMap); 1347 } 1348 1349 if (storageMap.has(propertyName)) { 1350 propertyLocal = storageMap.get(propertyName)!; 1351 } 1352 1353 if (propertyLocal) { 1354 return this.getOrNewPagNode(-1, propertyLocal, stmt); 1355 } 1356 return undefined; 1357 } 1358 1359 /** 1360 * add PagEdge 1361 * @param edgeKind: edge kind differs from API 1362 * @param propertyNode: PAG node created by protpertyName 1363 * @param obj: heapObj stored with Storage API 1364 */ 1365 public addPropertyLinkEdge(propertyNode: PagNode, storageObj: Value, cid: ContextID, stmt: Stmt, edgeKind: number): boolean { 1366 if (!(storageObj.getType() instanceof ClassType)) { 1367 return false; 1368 } 1369 1370 if (edgeKind === StorageLinkEdgeType.Property2Local) { 1371 // propertyNode --> objNode 1372 this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, storageObj), PagEdgeKind.Copy, stmt); 1373 } else if (edgeKind === StorageLinkEdgeType.Local2Property) { 1374 // propertyNode <-- objNode 1375 this.pag.addPagEdge(this.pag.getOrNewNode(cid, storageObj), propertyNode, PagEdgeKind.Copy, stmt); 1376 } else if (edgeKind === StorageLinkEdgeType.TwoWay) { 1377 // propertyNode <-> objNode 1378 this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, storageObj), PagEdgeKind.Copy, stmt); 1379 this.pag.addPagEdge(this.pag.getOrNewNode(cid, storageObj), propertyNode, PagEdgeKind.Copy, stmt); 1380 } 1381 return true; 1382 } 1383 1384 /* 1385 * In ArkIR, ArkField has multiple instances for each stmt which use it 1386 * But the unique one is needed for pointer analysis 1387 * This is a temp solution to use a ArkField->(first instance) 1388 * as the unique instance 1389 * 1390 * node merge condition: 1391 * instance field: value and ArkField 1392 * static field: ArkField 1393 */ 1394 public getRealInstanceRef(v: Value): Value { 1395 if (!(v instanceof ArkInstanceFieldRef || v instanceof ArkStaticFieldRef)) { 1396 return v; 1397 } 1398 1399 let sig = v.getFieldSignature(); 1400 let sigStr = sig.toString(); 1401 let base: Local; 1402 let real: Value | undefined; 1403 1404 if (v instanceof ArkInstanceFieldRef) { 1405 base = (v as ArkInstanceFieldRef).getBase(); 1406 if (base instanceof Local && base.getName() === GLOBAL_THIS_NAME && base.getDeclaringStmt() == null) { 1407 // replace the base in fieldRef 1408 base = this.getGlobalThisValue(); 1409 (v as ArkInstanceFieldRef).setBase(base as Local); 1410 } 1411 let key = `${base.getSignature()}-${sigStr}`; 1412 1413 real = this.instanceField2UniqInstanceMap.get(key); 1414 if (!real) { 1415 this.instanceField2UniqInstanceMap.set(key, v); 1416 real = v; 1417 } 1418 } else { 1419 real = this.staticField2UniqInstanceMap.get(sigStr); 1420 if (!real) { 1421 this.staticField2UniqInstanceMap.set(sigStr, v); 1422 real = v; 1423 } 1424 } 1425 return real; 1426 } 1427 1428 /** 1429 * check if a method is singleton function 1430 * rule: static method, assign heap obj to global var or static field, return the receiver 1431 */ 1432 public isSingletonFunction(funcID: FuncID): boolean { 1433 if (this.singletonFuncMap.has(funcID)) { 1434 return this.singletonFuncMap.get(funcID)!; 1435 } 1436 1437 let arkMethod = this.cg.getArkMethodByFuncID(funcID); 1438 if (!arkMethod) { 1439 this.singletonFuncMap.set(funcID, false); 1440 return false; 1441 } 1442 1443 if (!arkMethod.isStatic()) { 1444 this.singletonFuncMap.set(funcID, false); 1445 return false; 1446 } 1447 1448 let funcPag = this.funcPags.get(funcID)!; 1449 let heapObjects = [...funcPag.getInternalEdges()!].filter(edge => edge.kind === PagEdgeKind.Address).map(edge => edge.dst); 1450 1451 let returnValues = arkMethod.getReturnValues(); 1452 1453 let result = this.isValueConnected([...funcPag.getInternalEdges()!], heapObjects, returnValues); 1454 this.singletonFuncMap.set(funcID, result); 1455 if (result) { 1456 logger.info(`function ${funcID} is marked as singleton function`); 1457 } 1458 return result; 1459 } 1460 1461 private isValueConnected(edges: IntraProceduralEdge[], leftNodes: Value[], targetNodes: Value[]): boolean { 1462 // build funcPag graph 1463 const graph = new Map<Value, Value[]>(); 1464 let hasStaticFieldOrGlobalVar: boolean = false; 1465 1466 for (const edge of edges) { 1467 let dst = this.getRealInstanceRef(edge.dst); 1468 let src = this.getRealInstanceRef(edge.src); 1469 if (!graph.has(dst)) { 1470 graph.set(dst, []); 1471 } 1472 if (!graph.has(src)) { 1473 graph.set(src, []); 1474 } 1475 1476 if (dst instanceof ArkStaticFieldRef || src instanceof ArkStaticFieldRef) { 1477 hasStaticFieldOrGlobalVar = true; 1478 } 1479 1480 graph.get(src)!.push(dst); 1481 } 1482 1483 if (!hasStaticFieldOrGlobalVar) { 1484 return false; 1485 } 1486 1487 for (const targetNode of targetNodes) { 1488 for (const leftNode of leftNodes) { 1489 const visited = new Set<Value>(); 1490 let meetStaticField = false; 1491 if (this.funcPagDfs(graph, visited, leftNode, targetNode, meetStaticField)) { 1492 return true; // a value pair that satisfy condition 1493 } 1494 1495 if (!meetStaticField) { 1496 break; // heap obj will not deal any more 1497 } 1498 } 1499 } 1500 1501 return false; 1502 } 1503 1504 private funcPagDfs(graph: Map<Value, Value[]>, visited: Set<Value>, currentNode: Value, targetNode: Value, staticFieldFound: boolean): boolean { 1505 if (currentNode === targetNode) { 1506 return staticFieldFound; 1507 } 1508 1509 visited.add(currentNode); 1510 1511 for (const neighbor of graph.get(currentNode) || []) { 1512 // TODO: add global variable 1513 const isSpecialNode = neighbor instanceof ArkStaticFieldRef; 1514 1515 if (!visited.has(neighbor)) { 1516 if (isSpecialNode) { 1517 staticFieldFound = true; 1518 } 1519 1520 if (this.funcPagDfs(graph, visited, neighbor, targetNode, staticFieldFound)) { 1521 return true; 1522 } 1523 } 1524 } 1525 1526 return false; 1527 } 1528 1529 public getGlobalThisValue(): Local { 1530 return this.globalThisValue; 1531 } 1532 1533 private getEdgeKindForAssignStmt(stmt: ArkAssignStmt): PagEdgeKind { 1534 if (this.stmtIsCreateAddressObj(stmt)) { 1535 return PagEdgeKind.Address; 1536 } 1537 1538 if (this.stmtIsCopyKind(stmt)) { 1539 return PagEdgeKind.Copy; 1540 } 1541 1542 if (this.stmtIsReadKind(stmt)) { 1543 return PagEdgeKind.Load; 1544 } 1545 1546 if (this.stmtIsWriteKind(stmt)) { 1547 return PagEdgeKind.Write; 1548 } 1549 1550 return PagEdgeKind.Unknown; 1551 } 1552 1553 /** 1554 * process Storage API 1555 * @returns boolean: check if the cs represent a Storage API, no matter the API will success or fail 1556 */ 1557 private processStorage(cs: ICallSite, calleeCGNode: CallGraphNode, cid: ContextID): boolean { 1558 let storageName = calleeCGNode.getMethod().getDeclaringClassSignature().getClassName(); 1559 let storageType: StorageType = this.getStorageType(storageName, cs, cid); 1560 1561 // TODO: add other storages 1562 if (storageType === StorageType.APP_STORAGE) { 1563 let calleeName = calleeCGNode.getMethod().getMethodSubSignature().getMethodName(); 1564 1565 // TODO: complete AppStorage API 1566 if (calleeName === 'setOrCreate') { 1567 this.processStorageSetOrCreate(cs, cid); 1568 } else if (calleeName === 'link') { 1569 this.processStorageLink(cs, cid); 1570 } else if (calleeName === 'prop') { 1571 this.processStorageProp(cs, cid); 1572 } else if (calleeName === 'set') { 1573 this.processStorageSet(cs, cid); 1574 } else if (calleeName === 'get') { 1575 this.processStorageGet(cs, cid); 1576 } 1577 return true; 1578 } else if (storageType === StorageType.LOCAL_STORAGE) { 1579 // TODO: LocalStorage is not Static 1580 } 1581 1582 return false; 1583 } 1584 1585 private processStorageSetOrCreate(cs: ICallSite, cid: ContextID): void { 1586 let propertyStr = this.getPropertyName(cs.args![0]); 1587 if (!propertyStr) { 1588 return; 1589 } 1590 1591 let propertyName = propertyStr; 1592 let propertyNode = this.getOrNewPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt); 1593 let storageObj = cs.args![1]; 1594 1595 this.addPropertyLinkEdge(propertyNode, storageObj, cid, cs.callStmt, StorageLinkEdgeType.Local2Property); 1596 } 1597 1598 private processStorageLink(cs: ICallSite, cid: ContextID): void { 1599 let propertyStr = this.getPropertyName(cs.args![0]); 1600 if (!propertyStr) { 1601 return; 1602 } 1603 1604 let propertyName = propertyStr; 1605 let propertyNode = this.getOrNewPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt); 1606 let leftOp = (cs.callStmt as ArkAssignStmt).getLeftOp() as Local; 1607 let linkedOpNode = this.pag.getOrNewNode(cid, leftOp) as PagNode; 1608 if (linkedOpNode instanceof PagLocalNode) { 1609 linkedOpNode.setStorageLink(StorageType.APP_STORAGE, propertyName); 1610 } 1611 1612 this.pag.addPagEdge(propertyNode, linkedOpNode, PagEdgeKind.Copy); 1613 this.pag.addPagEdge(linkedOpNode, propertyNode, PagEdgeKind.Copy); 1614 } 1615 1616 private processStorageProp(cs: ICallSite, cid: ContextID): void { 1617 let propertyStr = this.getPropertyName(cs.args![0]); 1618 if (!propertyStr) { 1619 return; 1620 } 1621 1622 let propertyName = propertyStr; 1623 let propertyNode = this.getOrNewPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt); 1624 let leftOp = (cs.callStmt as ArkAssignStmt).getLeftOp() as Local; 1625 let linkedOpNode = this.pag.getOrNewNode(cid, leftOp) as PagNode; 1626 if (linkedOpNode instanceof PagLocalNode) { 1627 linkedOpNode.setStorageLink(StorageType.APP_STORAGE, propertyName); 1628 } 1629 1630 this.pag.addPagEdge(propertyNode, linkedOpNode, PagEdgeKind.Copy); 1631 } 1632 1633 private processStorageSet(cs: ICallSite, cid: ContextID): void { 1634 let ivkExpr: AbstractInvokeExpr = cs.callStmt.getInvokeExpr()!; 1635 if (ivkExpr instanceof ArkInstanceInvokeExpr) { 1636 let base = ivkExpr.getBase(); 1637 let baseNode = this.pag.getOrNewNode(cid, base) as PagLocalNode; 1638 1639 if (baseNode.isStorageLinked()) { 1640 let argsNode = this.pag.getOrNewNode(cid, cs.args![0]) as PagNode; 1641 1642 this.pag.addPagEdge(argsNode, baseNode, PagEdgeKind.Copy); 1643 } 1644 } else if (ivkExpr instanceof ArkStaticInvokeExpr) { 1645 // TODO: process AppStorage.set() 1646 } 1647 } 1648 1649 private processStorageGet(cs: ICallSite, cid: ContextID): void { 1650 if (!(cs.callStmt instanceof ArkAssignStmt)) { 1651 return; 1652 } 1653 let leftOp = (cs.callStmt as ArkAssignStmt).getLeftOp() as Local; 1654 let ivkExpr = cs.callStmt.getInvokeExpr(); 1655 let propertyName!: string; 1656 if (ivkExpr instanceof ArkStaticInvokeExpr) { 1657 let propertyStr = this.getPropertyName(cs.args![0]); 1658 if (propertyStr) { 1659 propertyName = propertyStr; 1660 } 1661 } else if (ivkExpr instanceof ArkInstanceInvokeExpr) { 1662 let baseNode = this.pag.getOrNewNode(cid, ivkExpr.getBase()) as PagLocalNode; 1663 if (baseNode.isStorageLinked()) { 1664 propertyName = baseNode.getStorage().PropertyName!; 1665 } 1666 } 1667 1668 let propertyNode = this.getPropertyNode(StorageType.APP_STORAGE, propertyName, cs.callStmt); 1669 if (!propertyNode) { 1670 return; 1671 } 1672 1673 this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, leftOp, cs.callStmt), PagEdgeKind.Copy, cs.callStmt); 1674 } 1675 1676 private getPropertyName(value: Value): string | undefined { 1677 if (value instanceof Local) { 1678 let type = value.getType(); 1679 if (type instanceof StringType) { 1680 return type.getName(); 1681 } 1682 } else if (value instanceof Constant) { 1683 return value.getValue(); 1684 } 1685 1686 return undefined; 1687 } 1688 1689 /** 1690 * get storageType enum with method's Declaring ClassName 1691 * 1692 * @param storageName ClassName that method belongs to, currently support AppStorage and SubscribedAbstractProperty 1693 * SubscribedAbstractProperty: in following listing, `link1` is infered as ClassType `SubscribedAbstractProperty`, 1694 * it needs to get PAG node to check the StorageType 1695 * let link1: SubscribedAbstractProperty<A> = AppStorage.link('PropA'); 1696 * link1.set(a); 1697 * @param cs: for search PAG node in SubscribedAbstractProperty 1698 * @param cid: for search PAG node in SubscribedAbstractProperty 1699 * @returns StorageType enum 1700 */ 1701 private getStorageType(storageName: string, cs: ICallSite, cid: ContextID): StorageType { 1702 switch (storageName) { 1703 case 'AppStorage': 1704 return StorageType.APP_STORAGE; 1705 case 'SubscribedAbstractProperty': { 1706 let calleeBaseLocal = (cs.callStmt.getInvokeExpr() as ArkInstanceInvokeExpr).getBase(); 1707 let calleeBaseLocalNode = this.pag.getOrNewNode(cid, calleeBaseLocal) as PagLocalNode; 1708 if (calleeBaseLocalNode.isStorageLinked()) { 1709 let storage = calleeBaseLocalNode.getStorage(); 1710 1711 return storage.StorageType!; 1712 } 1713 return StorageType.Undefined; 1714 } 1715 default: 1716 return StorageType.Undefined; 1717 } 1718 } 1719 1720 /**\ 1721 * ArkNewExpr, ArkNewArrayExpr, function ptr, globalThis 1722 */ 1723 private stmtIsCreateAddressObj(stmt: ArkAssignStmt): boolean { 1724 let lhOp = stmt.getLeftOp(); 1725 let rhOp = stmt.getRightOp(); 1726 if ( 1727 rhOp instanceof ArkNewExpr || 1728 rhOp instanceof ArkNewArrayExpr || 1729 (lhOp instanceof Local && 1730 ((rhOp instanceof Local && rhOp.getType() instanceof FunctionType && rhOp.getDeclaringStmt() === null) || 1731 (rhOp instanceof AbstractFieldRef && rhOp.getType() instanceof FunctionType))) || 1732 (rhOp instanceof Local && rhOp.getName() === GLOBAL_THIS_NAME && rhOp.getDeclaringStmt() == null) 1733 ) { 1734 return true; 1735 } 1736 1737 // TODO: add other Address Obj creation 1738 // like static object 1739 return false; 1740 } 1741 1742 private stmtIsCopyKind(stmt: ArkAssignStmt): boolean { 1743 let lhOp = stmt.getLeftOp(); 1744 let rhOp = stmt.getRightOp(); 1745 1746 let condition: boolean = 1747 (lhOp instanceof Local && 1748 (rhOp instanceof Local || rhOp instanceof ArkParameterRef || rhOp instanceof ArkThisRef || rhOp instanceof ArkStaticFieldRef)) || 1749 (lhOp instanceof ArkStaticFieldRef && rhOp instanceof Local); 1750 1751 if (condition) { 1752 return true; 1753 } 1754 return false; 1755 } 1756 1757 private stmtIsWriteKind(stmt: ArkAssignStmt): boolean { 1758 let lhOp = stmt.getLeftOp(); 1759 let rhOp = stmt.getRightOp(); 1760 1761 if (rhOp instanceof Local && (lhOp instanceof ArkInstanceFieldRef || lhOp instanceof ArkArrayRef)) { 1762 return true; 1763 } 1764 return false; 1765 } 1766 1767 private stmtIsReadKind(stmt: ArkAssignStmt): boolean { 1768 let lhOp = stmt.getLeftOp(); 1769 let rhOp = stmt.getRightOp(); 1770 1771 if (lhOp instanceof Local && (rhOp instanceof ArkInstanceFieldRef || rhOp instanceof ArkArrayRef)) { 1772 return true; 1773 } 1774 return false; 1775 } 1776 1777 public addToDynamicCallSite(funcPag: FuncPag, cs: DynCallSite): void { 1778 funcPag.addDynamicCallSite(cs); 1779 this.pagStat.numDynamicCall++; 1780 1781 logger.trace('[add dynamic callsite] ' + cs.callStmt.toString() + ': ' + cs.callStmt.getCfg()?.getDeclaringMethod().getSignature().toString()); 1782 } 1783 1784 public setPtForNode(node: NodeID, pts: IPtsCollection<NodeID> | undefined): void { 1785 if (!pts) { 1786 return; 1787 } 1788 1789 (this.pag.getNode(node) as PagNode).setPointTo(pts); 1790 } 1791 1792 public getRealThisLocal(input: Local, funcId: FuncID): Local { 1793 if (input.getName() !== 'this') { 1794 return input; 1795 } 1796 let real = input; 1797 1798 let f = this.cg.getArkMethodByFuncID(funcId); 1799 f 1800 ?.getCfg() 1801 ?.getStmts() 1802 .forEach(s => { 1803 if (s instanceof ArkAssignStmt && s.getLeftOp() instanceof Local) { 1804 if ((s.getLeftOp() as Local).getName() === 'this') { 1805 real = s.getLeftOp() as Local; 1806 return; 1807 } 1808 } 1809 }); 1810 return real; 1811 } 1812 1813 public doStat(): void { 1814 this.pagStat.numTotalNode = this.pag.getNodeNum(); 1815 } 1816 1817 public printStat(): void { 1818 this.pagStat.printStat(); 1819 } 1820 1821 public getStat(): string { 1822 return this.pagStat.getStat(); 1823 } 1824 1825 public getUnhandledFuncs(): FuncID[] { 1826 let handledFuncs = this.getHandledFuncs(); 1827 let unhandleFuncs = Array.from(this.cg.getNodesIter()) 1828 .filter(f => !handledFuncs.includes(f.getID())) 1829 .map(f => f.getID()); 1830 return unhandleFuncs; 1831 } 1832 1833 public getHandledFuncs(): FuncID[] { 1834 return Array.from(this.funcPags.keys()); 1835 } 1836 1837 /** 1838 * build export edge in internal func pag 1839 * @param value: Value that need to check if it is from import/export 1840 * @param originValue: if Value if InstanceFieldRef, the base will be passed to `value` recursively, 1841 * fieldRef will be passed to `originValue` 1842 */ 1843 private handleValueFromExternalScope(value: Value, funcID: FuncID, originValue?: Value): void { 1844 if (value instanceof Local) { 1845 if (value.getDeclaringStmt() || value.getName() === 'this') { 1846 // not from external scope 1847 return; 1848 } 1849 1850 if (!value.getType()) { 1851 return; 1852 } 1853 1854 let srcLocal = this.getSourceValueFromExternalScope(value, funcID); 1855 1856 if (srcLocal) { 1857 // if `value` is from field base, use origin value(fieldRef) instead 1858 this.addInterFuncEdge(srcLocal, originValue ?? value, funcID); 1859 } 1860 } else if (value instanceof ArkInstanceFieldRef) { 1861 let base = value.getBase(); 1862 if (base) { 1863 this.handleValueFromExternalScope(base, funcID, value); 1864 } 1865 } 1866 } 1867 1868 private addInterFuncEdge(src: Local, dst: Value, funcID: FuncID): void { 1869 this.interFuncPags = this.interFuncPags ?? new Map(); 1870 let interFuncPag = this.interFuncPags.get(funcID) ?? new InterFuncPag(); 1871 // Export a local 1872 // Add a InterProcedural edge 1873 if (dst instanceof Local) { 1874 let e: InterProceduralEdge = { 1875 src: src, 1876 dst: dst, 1877 kind: PagEdgeKind.InterProceduralCopy, 1878 }; 1879 interFuncPag.addToInterProceduralEdgeSet(e); 1880 this.addExportVariableMap(src, dst as Local); 1881 } else if (dst instanceof ArkInstanceFieldRef) { 1882 // record the export base use 1883 this.addExportVariableMap(src, dst.getBase()); 1884 } 1885 this.interFuncPags.set(funcID, interFuncPag); 1886 1887 // Put the function which the src belongs to to worklist 1888 let srcFunc = src.getDeclaringStmt()?.getCfg().getDeclaringMethod(); 1889 if (srcFunc) { 1890 let srcFuncID = this.cg.getCallGraphNodeByMethod(srcFunc.getSignature()).getID(); 1891 let cid = this.ctx.getNewContextID(srcFuncID); 1892 let csFuncID = new CSFuncID(cid, srcFuncID); 1893 this.buildFuncPagAndAddToWorklist(csFuncID); 1894 } 1895 // Extend other types of src here 1896 } 1897 1898 private getSourceValueFromExternalScope(value: Local, funcID: FuncID): Local | undefined { 1899 let sourceValue; 1900 1901 sourceValue = this.getDefaultMethodSourceValue(value, funcID); 1902 if (!sourceValue) { 1903 sourceValue = this.getExportSourceValue(value, funcID); 1904 } 1905 1906 return sourceValue; 1907 } 1908 1909 private getDefaultMethodSourceValue(value: Local, funcID: FuncID): Local | undefined { 1910 // namespace check 1911 let arkMethod = this.cg.getArkMethodByFuncID(funcID); 1912 if (!arkMethod) { 1913 return undefined; 1914 } 1915 1916 let declaringNameSpace = arkMethod.getDeclaringArkClass().getDeclaringArkNamespace(); 1917 while (declaringNameSpace) { 1918 let nameSpaceLocals = declaringNameSpace.getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals() ?? new Map(); 1919 if (nameSpaceLocals.has(value.getName())) { 1920 return nameSpaceLocals.get(value.getName()); 1921 } 1922 1923 declaringNameSpace = declaringNameSpace.getDeclaringArkNamespace() ?? undefined; 1924 } 1925 1926 // file check 1927 let declaringFile = arkMethod.getDeclaringArkFile(); 1928 let fileLocals = declaringFile.getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals() ?? new Map(); 1929 if (!fileLocals.has(value.getName())) { 1930 return undefined; 1931 } 1932 1933 return fileLocals.get(value.getName()); 1934 } 1935 1936 private getExportSourceValue(value: Local, funcID: FuncID): Local | undefined { 1937 let curMethod = this.cg.getArkMethodByFuncID(funcID); 1938 if (!curMethod) { 1939 return undefined; 1940 } 1941 1942 let curFile = curMethod.getDeclaringArkFile(); 1943 let impInfo = curFile.getImportInfoBy(value.getName()); 1944 if (!impInfo) { 1945 return undefined; 1946 } 1947 1948 let exportSource = impInfo.getLazyExportInfo(); 1949 if (!exportSource) { 1950 return undefined; 1951 } 1952 1953 let exportSouceValue = exportSource.getArkExport(); 1954 if (exportSouceValue instanceof Local) { 1955 return exportSouceValue; 1956 } 1957 return undefined; 1958 } 1959 1960 private addExportVariableMap(src: Local, dst: Local): void { 1961 let exportMap: Local[] = this.externalScopeVariableMap.get(src) ?? []; 1962 if (!exportMap.includes(dst)) { 1963 exportMap.push(dst); 1964 this.externalScopeVariableMap.set(src, exportMap); 1965 } 1966 } 1967 1968 public getExportVariableMap(src: Local): Local[] { 1969 return this.externalScopeVariableMap.get(src) ?? []; 1970 } 1971 1972 /// Add inter-procedural Pag Nodes and Edges 1973 public addEdgesFromInterFuncPag(interFuncPag: InterFuncPag, cid: ContextID): boolean { 1974 let edges = interFuncPag.getInterProceduralEdges(); 1975 if (edges.size === 0) { 1976 return false; 1977 } 1978 1979 for (let e of edges) { 1980 // Existing local exported nodes -> ExportNode 1981 let exportLocal = e.src; 1982 let dstPagNode = this.getOrNewPagNode(cid, e.dst); 1983 1984 // get export local node in all cid 1985 let existingNodes = this.pag.getNodesByValue(exportLocal); 1986 existingNodes?.forEach(n => { 1987 this.pag.addPagEdge(this.pag.getNode(n)! as PagNode, dstPagNode, e.kind); 1988 this.retriggerNodesList.add(n); 1989 }); 1990 } 1991 1992 return true; 1993 } 1994 1995 public getRetriggerNodes(): NodeID[] { 1996 let retriggerNodes = Array.from(this.retriggerNodesList); 1997 this.retriggerNodesList.clear(); 1998 return retriggerNodes; 1999 } 2000 2001 public addUpdatedNode(nodeID: NodeID, diffPT: IPtsCollection<NodeID>): void { 2002 let ptaConfig = PointerAnalysisConfig.getInstance(); 2003 let updatedNode = this.updatedNodesThisRound.get(nodeID) ?? new ptaConfig.ptsCollectionCtor(); 2004 updatedNode.union(diffPT); 2005 this.updatedNodesThisRound.set(nodeID, updatedNode); 2006 } 2007 2008 public getUpdatedNodes(): Map<number, IPtsCollection<number>> { 2009 return this.updatedNodesThisRound; 2010 } 2011 2012 public resetUpdatedNodes(): void { 2013 this.updatedNodesThisRound.clear(); 2014 } 2015 2016 private transferArrayValues(method: ArkMethod, arrayLocal: Value): Local[] { 2017 if (!(arrayLocal instanceof Local) || !(arrayLocal.getType() instanceof ArrayType)) { 2018 return []; 2019 } 2020 2021 /** 2022 * TODO: get array element values 2023 * need to resolve multi dimension array 2024 */ 2025 const usedValuesInArray = arrayLocal.getUsedStmts().flatMap(stmt => { 2026 if (stmt instanceof ArkAssignStmt) { 2027 const rightOp = stmt.getRightOp(); 2028 if (rightOp instanceof Local) { 2029 return rightOp; 2030 } 2031 } 2032 return []; 2033 }); 2034 2035 return usedValuesInArray; 2036 } 2037} 2038