我试图通过删除代码中的if语句来解决时间泄漏,但是由于c ++对if语句中的整数输入的解释,我被卡住了。
请注意,我假设编译器确实创建了条件分支,导致时间信息泄露!
原始代码是:
int s
if (s)
r = A
else
r = B
现在我试图将其重写为:
int s;
r = sA+(1-s)B
因为s没有绑定到[0,1],所以如果s超出[0,1],我会遇到错误地乘以A和B的问题。如果不在s上使用if语句来解决这个问题,我该怎么办?
提前致谢
答案 0 :(得分:1)
你有什么证据表明if语句会导致时间泄漏?
如果使用启用了优化的现代编译器,则该代码不应生成分支。您应该通过查看汇编语言输出来检查编译器正在做什么。
例如,g ++ 5.3.0编译此代码:
#include <Windows.h>
#include <iostream>
#include <iomanip>
#include <exception>
#include <cstdint>
#include <vector>
#include <sstream>
#include <fstream>
template <typename T>
void print_hex(std::ostream &stream, T x, int width = 8){
stream << std::hex << std::setw(width) << std::setfill('0') << x << std::dec;
}
template <typename T>
void print_address(std::ostream &stream, T x){
if (x < 0x100)
print_hex(stream, x, 2);
else if (x < 0x10000)
print_hex(stream, x, 4);
else if (x < 0x100000000ULL)
print_hex(stream, x, 8);
else
print_hex(stream, x, 16);
}
class DebugProcess{
DWORD pid;
public:
DebugProcess(DWORD pid): pid(pid){
if (!DebugActiveProcess(pid)){
auto error = GetLastError();
std::cerr << "DebugActiveProcess() failed with error " << error << " (0x";
print_hex(std::cerr, error);
std::cerr << ")\n";
throw std::exception();
}
}
~DebugProcess(){
if (!DebugActiveProcessStop(this->pid)){
auto error = GetLastError();
std::cerr << "DebugActiveProcessStop() failed with error " << error << " (0x";
print_hex(std::cerr, error);
std::cerr << ")\n";
}
}
};
bool is_handle_valid(HANDLE handle){
return handle && handle != INVALID_HANDLE_VALUE;
}
class AutoHandle{
HANDLE handle;
public:
AutoHandle(HANDLE handle): handle(handle){}
~AutoHandle(){
if (is_handle_valid(this->handle))
CloseHandle(this->handle);
}
};
template <typename T>
void zero_struct(T &mem){
memset(&mem, 0, sizeof(mem));
}
struct memory_region{
std::uint64_t start,
size;
MEMORY_BASIC_INFORMATION info;
};
void dump_process_memory(DWORD pid){
DebugProcess dp(pid);
auto proc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
if (!is_handle_valid(proc)){
auto error = GetLastError();
std::cerr << "OpenProcess() failed with error " << error << " (0x";
print_hex(std::cerr, error);
std::cerr << ")\n";
return;
}
AutoHandle autoproc(proc);
std::vector<memory_region> regions;
for (std::uint64_t address = 0; address < 0x10000000ULL;){
MEMORY_BASIC_INFORMATION mbi;
zero_struct(mbi);
auto bytes = VirtualQueryEx(proc, (LPCVOID)address, &mbi, sizeof(mbi));
if (!bytes){
address += 4096;
continue;
}
if (mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_GUARD) != PAGE_GUARD)
regions.push_back(memory_region{ (std::uint64_t)mbi.BaseAddress, mbi.RegionSize, mbi });
address += mbi.RegionSize;
}
if (regions.size()){
std::cout << "Flat size: " << regions.back().start + regions.back().size << std::endl;
std::uint64_t sum = 0;
for (auto ®ion : regions)
sum += region.size;
std::cout << "Packed size: " << sum << std::endl;
}
std::ofstream file("dump.bin", std::ios::binary);
std::uint64_t current_size = 0;
for (auto ®ion : regions){
std::vector<char> buffer(region.size);
size_t read;
if (!ReadProcessMemory(proc, (LPCVOID)region.start, &buffer[0], buffer.size(), &read)){
auto error = GetLastError();
if (error != ERROR_PARTIAL_COPY){
std::cerr << "ReadProcessMemory() failed with error " << error << " (0x";
print_hex(std::cerr, error);
std::cerr << ")\n";
return;
}
}
if (read < region.size){
#if 1
std::cerr << "Warning: region starting at 0x";
print_address(std::cerr, region.start);
std::cerr << " has size " << region.size << ", but only " << read
<< " bytes could be read by ReadProcessMemory().\n";
#endif
memset(&buffer[read], 0, buffer.size() - read);
}
file.seekp(region.start);
file.write(&buffer[0], buffer.size());
}
}
int main(int argc, char **argv)
{
DWORD pid;
std::cout << "Enter PID : ";
std::cin >> pid;
try{
dump_process_memory(pid);
}catch (std::exception &){
std::cerr << "Exception caught.\n";
}
std::cin.ignore();
std::cin.get();
return 0;
}
到这个集会:
int f(int s, int A, int B) {
int r;
if (s)
r = A;
else
r = B;
return r;
}
看,妈!没有分支! ;)
答案 1 :(得分:1)
如果你知道整数中的位数,那就很容易了,虽然有一些复杂因素使得标准清理可能会出现异常的整数表示。
这是32位整数的一个简单解决方案:
uint32_t mask = s;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
mask &= 1;
r = b ^ (-mask & (a ^ b)):
五个移位和/或语句传播mask
中的任何设置位,以便最后低位为1,除非掩码最初为0.然后我们隔离低位,结果在1或0中。最后一个语句相当于你的两个乘法并加上。
根据观察,这是一个更快的一个,如果你从一个数字中减去一个并且符号位从0变为1,那么数字是0:
uint32_t mask = ((uint32_t(s)-1U)&~uint32_t(s))>>31) - 1U;
这与减去1然后使用进位位的计算基本相同,但不幸的是,进位位没有暴露给C语言(除非可能通过编译器特定的内在函数)。
其他变化是可能的。
答案 2 :(得分:-1)
在优化不可用时,没有分支的唯一方法是使用内联汇编。假设8086:
mov ax, s
neg ax ; CF = (ax != 0)
sbb ax, ax ; ax = (s != 0 ? -1 : 0)
neg ax ; ax = (s != 0 ? 1 : 0)
mov s, ax ; now use s at will, it will be: s = (s != 0 ? 1 : 0)