# openEuler 24.03 MATE 桌面登录要输两次密码?lightdm-gtk-greeter 空指针崩溃修复实录
# openEuler 24.03 MATE 桌面登录要输两次密码?lightdm-gtk-greeter 空指针崩溃修复实录
> 环境:openEuler 24.03 LTS SP4 x86_64,内核 6.6.0-159.4.10.164.oe2403sp4,MATE 1.28,
> lightdm 1.30.0-14,lightdm-gtk(lightdm-gtk-greeter)2.0.8-2,cairo 1.18.0-2
>
> 现象:每次开机在登录界面输入密码后,**总要重新输一次密码、登录两次才能进桌面**。
## 一、问题现象
- 开机进入登录界面 → 输入密码 → 屏幕一闪,登录界面重新出现 → 再次输入密码 → 成功进桌面。
- 每次开机必现,且第二次登录必然成功。
- `/var/log/lightdm/seat0-greeter.log` 反复出现:
```
(lightdm-gtk-greeter:1359): Gtk-WARNING: Drawing a gadget with negative dimensions. (node menubar owner GreeterMenuBar)
```
## 二、初步排查:确实是 greeter 崩溃,LightDM 自动重启登录界面
1. **coredump 证据**:`/var/lib/systemd/coredump/` 里每次开机都有一个新的 `core.lightdm-gtk-gre.*`,说明登录界面进程每次必崩一次。
2. **内核日志**(崩溃现场):
```
kernel: lightdm-gtk-gre[1359]: segfault at 10 ip ... in libcairo.so.2.11802.2 ...
```
3. **lightdm 日志**(`/var/log/lightdm/lightdm.log.old`)证实了"崩溃 → 重启"的完整链路:
```
[+188.72s] Continue authentication
[+190.46s] Authentication complete with return value 0: 成功 ← 密码已通过!
[+201.43s] Session pid=1334: Exited with return value 1 ← greeter 进程退出(崩溃)
[+201.43s] ... (随后 LightDM 重新拉起 greeter,用户被迫再次登录)
```
> 关键点:**第一次输入的密码其实已经验证通过了**,崩溃发生在认证成功之后、会话启动之前。
## 三、误入歧途:以为是 cairo 的 bug(排除过程)
崩溃地址在 `libcairo.so.2` 里,于是怀疑是 cairo 1.18.0 的缺陷(当时 openEuler 恰好有 CVE-2025-50422 的补丁,属于同一家族)。
- 把 cairo 升级到 openEuler 25.03 仓库的 **1.18.2**(`dnf install https://.../cairo-1.18.2-1.oe2503.x86_64.rpm`,注意要连同 `cairo-gobject` 一起升,否则版本锁冲突)→ **重启后依然崩溃**。
- 对比 cairo 1.18.0 与 1.18.2 源码,崩溃相关文件(`cairo-rectangle.c`/`cairo-spline.c`/`cairo-boxes.c`)**完全没变**,升级无效是必然的。
**结论:崩溃点虽然在 cairo 里,但根因不在 cairo,而是调用方传了空指针。**
## 四、决定性证据:gdb 解开 coredump 拿到调用栈
```bash
sudo dnf install -y gdb
sudo unlz4 -f /var/lib/systemd/coredump/core.lightdm-gtk-gre.*.lz4 /tmp/greeter-core
sudo gdb -batch -ex "bt" -ex "info registers" /usr/sbin/lightdm-gtk-greeter /tmp/greeter-core
```
调用栈:
```
#0 cairo_surface_get_type () in /usr/lib64/libcairo.so.2
#1 greeter_background_save_xroot () ← greeter 自己的背景保存函数
#2 内部回调
#7 liblightdm-gobject-1.so ← LightDM 信号触发(认证完成)
#13 main ()
```
寄存器:`rdi = 0x0` —— **`cairo_surface_get_type(NULL)` 传入了空指针**,读取 `surface->type`(偏移 0x10)→ 段错误(与内核日志 `segfault at 10` 完全吻合)。
## 五、根因定位(源码级)
`lightdm-gtk-greeter-2.0.8/src/greeterbackground.c` 中:
```c
void greeter_background_save_xroot(GreeterBackground* background)
{
...
surface = create_root_surface(priv->screen); // ← 返回 NULL!
cr = cairo_create(surface);
...
set_surface_as_root(priv->screen, surface); // ← 这里对 NULL 调 cairo_surface_get_type
}
```
`create_root_surface()` 返回 NULL 的唯一路径是 **`XOpenDisplay()` 失败**:
```c
static cairo_surface_t* create_root_surface(GdkScreen* screen)
{
...
display = XOpenDisplay (gdk_display_get_name (...));
if (!display)
{
g_warning("[Background] Failed to create root pixmap");
return NULL; // ← 崩溃前的警告
}
...
}
```
而 `set_surface_as_root()` 第一行就直接对参数取类型,**没有判空**:
```c
static void set_surface_as_root(GdkScreen* screen, cairo_surface_t* surface)
{
g_return_if_fail(cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB); // NULL → 段错误
...
}
```
崩溃前 greeter 日志的最后两行(实锤):
```
No protocol specified
** (lightdm-gtk-greeter:1359): WARNING: [Background] Failed to create root pixmap
```
**`No protocol specified` = XOpenDisplay 新连接被 X 服务器以 X 授权失败拒绝**(登录成功瞬间 LightDM 轮换/清理了授权文件,greeter 用旧 cookie 打不开新连接)。greeter 没处理 NULL → 段错误 → LightDM 重启登录界面 → 用户再输一次密码。
> 这是 **lightdm-gtk-greeter 上游至今未修的 bug**(2.0.9 同样存在),与 cairo 无关。
## 六、修复方案:打补丁重新编译
对 `greeterbackground.c` 加空指针检查(两处,防御性):
```diff
--- a/src/greeterbackground.c
+++ b/src/greeterbackground.c
@@ -1087,6 +1087,11 @@ greeter_background_save_xroot(GreeterBackground* background)
priv = background->priv;
surface = create_root_surface(priv->screen);
+ if (surface == NULL)
+ {
+ g_warning("[Background] Failed to create root surface, skipping");
+ return;
+ }
cr = cairo_create(surface);
gdk_cairo_set_source_rgba(cr, &ROOT_COLOR);
```
```diff
--- a/src/greeterbackground.c
+++ b/src/greeterbackground.c
@@ -1740,6 +1740,7 @@ set_surface_as_root(GdkScreen* screen, cairo_surface_t* surface)
Pixmap pixmap_id;
Window xroot;
+ g_return_if_fail(surface != NULL);
g_return_if_fail(cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB);
```
编译安装步骤(openEuler 24.03 SP4 实测可用):
```bash
# 1. 安装构建依赖
sudo dnf install -y gtk3-devel lightdm-gobject-devel automake autoconf libtool \
intltool gettext-devel gobject-introspection-devel xfce4-dev-tools
# 2. 下载源码(2.0.8,官方 tag)
curl -L -o lgg208.tar.gz https://codeload.github.com/Xubuntu/lightdm-gtk-greeter/tar.gz/refs/tags/lightdm-gtk-greeter-2.0.8
tar xzf lgg208.tar.gz && cd lightdm-gtk-greeter-lightdm-gtk-greeter-2.0.8
# 3. 应用上面的补丁(编辑 src/greeterbackground.c)
# 4. 构建(注意:2.0.8 是 GTK3 版,需要 gtk3-devel 而非 gtk2-devel)
autoreconf -fi
./configure --prefix=/usr
make -j$(nproc)
# 若提示 xdt-csource 生成头文件失败,手工生成:
# cd src && xdt-csource --static --strip-comments --strip-content --name=lightdm_gtk_greeter_ui lightdm-gtk-greeter.glade > lightdm-gtk-greeter-ui.h && cd ..
# 5. 备份并安装
sudo cp /usr/sbin/lightdm-gtk-greeter /usr/sbin/lightdm-gtk-greeter.orig
sudo cp src/lightdm-gtk-greeter /usr/sbin/lightdm-gtk-greeter
# 6. 重启验证
sudo reboot
```
## 七、验证结果
- 重启后**输入一次密码直接进桌面**。
- `/var/lib/systemd/coredump/` 不再产生新的崩溃记录。
- 登录界面外观、功能与原来完全一致(该补丁只在"创建根窗口背景失败"时跳过该步骤,不影响任何正常功能)。
## 八、给维护者的建议
1. 该修复(`create_root_surface()` 返回值判空)应提交到上游 https://github.com/Xubuntu/lightdm-gtk-greeter,2.0.9 仍有此问题。
2. 另外建议关注 `GreeterMenuBar` 的 "negative dimensions" 布局警告(Debian Bug #1085346),虽不致命但会刷爆 `seat0-greeter.log`。
3. 顺带一提:排查中还发现 X 授权文件在会话启动瞬间会被 LightDM 轮换,导致 greeter 的新 X 连接失败——若上游愿意,也可以研究让 `save_xroot` 复用现有连接而不是另开一个。
---
*附:本次排查用到的关键命令(gdb 调用栈、lightdm 日志、coredump 分析)已在文中给出;若你的发行版 lightdm-gtk-greeter 版本不同,补丁位置(`greeter_background_save_xroot`)不变,可对照源码适配。*
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)