Welcome¶
使用正则来校验输入合法性
软件加固里面经常使用的办法就是校验用户输入,例如要求名词需要是大小写或_开头, 支持包含大小写 ,中划线 -,下划线_以及点.
那么相应的正则表到式为^[a-zA-Z_][-a-zA-Z1-9_.]*$
,校验方法为
echo $1 | grep -q '^[a-zA-Z_][-a-zA-Z1-9_.]*$'
test $? -eq 0 && echo "yes" || echo "no"
用python实现
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> print(re.match('^[a-zA-Z_][a-zA-Z0-9\-_.]*$', '_abcdedf.', flags=0))
<re.Match object; span=(0, 9), match='_abcdedf.'>
>>> print(re.match('^[a-zA-Z_][a-zA-Z0-9\-_.]*$', '._abcdedf.', flags=0))
None
记录Git的重要操作
Git登录信息
在临时环境下拉代码, 如果重新生成密钥再添加到托管平台比较麻烦,要么拷贝自己的私钥过来,更加不安全。 这时候临时用密码登录最适合了, 但是每个pull fetch操作都要登录,这很烦躁,所以下面有2个方法解决
设置密码暂存
指类似sudo命令一样, 校验成功之后一段时间,不需要输入密码
git config --global credential.helper 'cache --timeout 900'
设置密码永存
不推荐
git config --global credential.helper store
git为不同项目配置不同的用户名和邮箱
电脑中同时有个人项目和公司项目时,有时候会导致commit 消息里面的邮件搞串了。可以通过git配置不同目录下的项目使用不同的信息。参考
C++ 小技巧
使用lambda 定义一个可变的const
因为const 不能改变的,只有在程序运行的时候初始化。 而通过lamda,可以通过获取命令行输入来改变值
const int x = []() -> int {
int t;
std::cin >> t;
return t;
}();
gdb调试容器内的进程
先来个gdb手册链接
这里记录一下自己常用的技巧
恢复debug符号
通常上线的程序要strip符号表来减少内存占用和硬盘占用,会执行以下命令
## 1. 只保留debug表的内容(可选),为以后调试用,生产时不用
objcopy --only-keep-debug foo foo.dbg
## 2. 将foo的debug表删除,不要任何调试信息,用于生产使用
objcopy --strip-debug foo
如果生产出问题了,就需要恢复debug信息,add-gnu-debuglink参数可以是foo.dbg, 也可以就是strip之前的foo文件
所以第一步是可选的
## 3. 恢复debug信息
objcopy --add-gnu-debuglink=foo.dbg foo
但其实第三步也是可选的,只要把foo.dbg放到foo相同位置时,gdb就可以读取到debug信息
使用.gdbinit
.gdbinit 文件是类似.bash_profile的文件,一般在home目录或者项目根目录, gdb 自动读取
也可以手动读取其他位置的, 在gdb执行source /somedir/.gdbinit
通常可以在这个文件里定义一些设置例如set pagination off
关闭输出分页, 就不用每次交互确定
还可以定义命令 如pvector可以打印vector内的所有成员, 非常比较方便
gdb 调试容器内的进程
因为容器内运行的程序都是strip了debug符号的,所以之前调试都是先将debug文件复制到容器对应位置,然后将源码也映射到容器内,而且还要安装gdb和重新开启priveledge模式, 非常繁琐。
所以尝试如下操作:
1. 能不能单独一个容器安装gdb和符号和代码文件, 跨容器去调试另一个容器的进程呢?
2. 能不能在宿主机上直接调试容器进程呢?
经过一番搜索,没有第一种方案的实践办法,但第二种方案亲测可行!
步骤
- 先启动容器,一般业务主进程的pid为1
- 在容器外面执行
ps aux | grep xxx
,因为在很多情况是多个容器跑同一个程序,所以ps aux
通过参数名来确定进程的pid, 当然最好的方式是通过进程namespace了 - 使用sudo gdb -p pid, 提示缺少
Missing separate debuginfo for target
- 将debug符号文件复制到容器对应容器的目录
- 再次attach,提示缺少源文件
/builds/project/src/aaa.cpp
,这是cmake编译时的目录,需要替换 - 在gdb里执行命令 set substitute-path /builds/ /home/my-home/ # from -> to
- 然后使用l可以查看对应的源代码了
总结
可以直接在宿主机上运行gdb来调试容器进程,并替换代码路径为宿主机的目录。
ps: gdb 启动时默认会读取~/.gdbinit 文件, 但是gdb会提示, 这是由于调试docker需要sudo权限,而我的gdbinit在非root目录
warning: File "/home/h/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /home/h/.gdbinit
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
也就是需要手动执行set auto-load safe-path /
或者将 add-auto-load-safe-path /home/h/.gdbinit
写入/root/.gdbinit
gcc 会优化很多内存相关的调用
他们通常在'string.h'里,比如memcpy, 当编译器通过上下文能推断出传入参数类型,libc的mempcy就会替换为gcc的memcpy, 这可能导致问题
举例
知乎评论的,release版本的memset被替换, 导致跟debug版本不同行为bug
https://www.zhihu.com/question/435258463/answer/1969561682
内存地址对齐
如果内存地址对齐的,那么'取值地址被取值长度求模为0',例如int i
, 则要求(void*)&i % sizeof(int) == 0
原因
当同时定义多个变量时,不考虑编译器优化,则他们在内存的位置相邻。按照不同的对齐长度,有不同的占用大小
这里以结构体举例
struct bar{
int interger;
char charactor;
long long longlong;
};
#pragma pack(4)
设置按4字节对齐,默认也是4字节,那么sizeof(struct abc) == 16
使用gdb打印内存地址,可见地址都对齐的: '0x7fffffffe430&4==0' '0x7fffffffe438%8==0'
(gdb) p sizeof(struct abc)
$5 = 16
(gdb) p &a.interger
$1 = (int *) 0x7fffffffe430
(gdb) p &a.charactor
$2 = 0x7fffffffe434 "\232\177"
(gdb) p &a.longlong
$3 = (long long *) 0x7fffffffe438
(gdb) p &a
$4 = (abc *) 0x7fffffffe430
顺便提一下,指针指向的是内存段的低地址,所以有个面试题是判断系统是大端小端的方法如下
通过如下强转int为char, 如果c==0x12
则为大端,否则为小端系统
int i=0x12345678;
char c=(char)i;
使用#pragma pack(1)
设置为按1字节对齐,则上面的结构体大小为'13',显然更省内存了
但通过取地址会发现地址未对齐, 求模不为0。 但我自己使用gcc测试地址依旧对齐的,原因是编译器会进行自然 对齐
自然对齐
上面的#pragma pack(1)
虽然使得longlong的地址对于bar来说偏移了5字节,但编译器始终能调整bar的起始地址使得longlong的地址对齐
595fac88
c3c5a2d8
105d3ab8
不对齐的坏处
虽然上面说了编译器能自然对齐,但还是会真的出现不对齐访问。由于对内存的访问涉及到具体硬件的实现,所以一般会有几种影响 1. 硬件平台支持非对齐访问,但性能有损耗 2. 抛出异常给cpu,由cpu来校准,性能有更大损耗 3. 平台不支持,但也不抛异常,获取的是错误的值,导致难定位的异常
引用
https://www.kernel.org/doc/html/latest/core-api/unaligned-memory-access.html
mysql sql语句优先级
The actual execution of MySQL statements is a bit tricky. However, the standard does specify the order of interpretation of elements in the query. This is basically in the order that you specify, although I think HAVING and GROUP BY could come after SELECT:
FROM clause
WHERE clause
SELECT clause
GROUP BY clause
HAVING clause
ORDER BY clause
This is important for understanding how queries are parsed. You cannot use a column alias defined in a SELECT in the WHERE clause, for instance, because the WHERE is parsed before the SELECT. On the other hand, such an alias can be in the ORDER BY clause.
As for actual execution, that is really left up to the optimizer. For instance:
. . .
GROUP BY a, b, c
ORDER BY NULL
and
. . .
GROUP BY a, b, c
ORDER BY a, b, c
both have the effect of the ORDER BY not being executed at all -- and so not executed after the GROUP BY (in the first case, the effect is to remove sorting from the GROUP BY and in the second the effect is to do nothing more than the GROUP BY already does).
引用
https://stackoverflow.com/questions/24127932/mysql-query-clause-execution-order