我为自由软件社区贡献的最初几步(现有项目)
2026-08-02T23:18:30+08:00
近些天来,我对自由软件社区的贡献远远少于我从中受益的程度,而我所做出的贡献也仅限于一些小而简单的个人项目以及非代码类贡献(例如一些翻译、向 Debian Screenshots 上传几张截图,以及为 Free Software Chinese Site 撰写文章)。在过去的一年里,我学习了 C/C++、Qt、汇编,以及一些 Rust。我认为,现在是时候为自由软件社区和自由软件运动做一些有意义的事情了。
免责声明:这不是一篇指南!我撰写这篇文章,只是为了分享我自己的经历。请参考相关文档,并向专家请教,以进一步了解如何为自由软件社区做出贡献。
我为什么选择 KDE
最初,我曾考虑为 postmarketOS 贡献。不过,我觉得自己的技能距离所需水平还差得很远,于是决定先从相对简单的事情开始。由于我对 C++ 和 Qt 更熟悉,所以我选择 KDE 作为我对社区的第一次贡献。
感受
我阅读源代码的能力并不强。大约在 2025 年初,我尝试阅读 PDF Arranger 的源代码,当时我感到非常难受,也有些不知所措。
不过,得益于学习逆向工程的经历,而这恰好需要阅读汇编代码,我阅读高级语言源代码的能力也有所提升。因此,我认为自己应该尝试一次真正的自由软件贡献,在真实环境中练习自己的技能。
在打开源码树之前,我有些紧张,担心自己会像读 PDF Arranger 的源代码时那样,被庞大的代码淹没。多亏了朋友 Integral 的鼓励,我才鼓起勇气迈出那一步。
工具
KDE 推荐的开发环境之一是最新版的 Fedora KDE。由于我使用的是 Qubes OS,所以我可以很方便地快速搭建一个独立的 HVM,并在其中完成所有工作。
随后,我按照 KDE 开发者的官方文档 配置了 kde-builder。
工作过程
我浏览了 KDE 官方 Bugzilla 上托管的漏洞列表。我注意到一个看起来非常容易修复的漏洞,但在拉取源代码并构建二进制文件之后,我发现这个漏洞在我当前环境下,使用最新版本的 Ark 无法复现。
随后我又回到漏洞列表,发现了另一个看起来很简单的漏洞。该漏洞报告中的系统环境和软件版本与我的相同(Fedora KDE,26.04),而且在 Fedora 官方仓库提供的应用程序中可以复现。确认这一点之后,我拉取了源代码并进行了构建。
我在二进制文件 ~/kde/build/konsole/bin/konsole 上启动了 GDB。把漏洞报告中提到的字符串(calt,liga,ss03, ss07,ss09)粘贴到输入框后,我在终端里看到了如下输出:
The tag name must be exactly 4 characters long!
在与一个 LLM 进行了几轮交流之后,我找到了一种用 GDB Python 脚本定位源码中打印这条提示的代码行的方法:
python
import gdb
TARGET = b"The tag name must be exactly 4 characters long!"
class FilteredWrite(gdb.Breakpoint):
def stop(self):
fd = int(gdb.parse_and_eval("$rdi"))
if fd not in (1, 2):
return False
buf = int(gdb.parse_and_eval("$rsi"))
count = int(gdb.parse_and_eval("$rdx"))
try:
data = gdb.selected_inferior().read_memory(buf, count).tobytes()
except gdb.MemoryError:
return False
if TARGET in data:
gdb.write("\n=== matched output ===\n")
gdb.write(data.decode("utf-8", "replace") + "\n")
gdb.execute("bt full")
return True
return False
FilteredWrite("write")
在一次 GDB 调试会话之后,我确认了这条警告是由 kwidgetsaddons 中的 kfontchooser.cpp 文件触发的。我还找到了触发这条警告的函数:
void KFontChooserPrivate::slotFeaturesChanged(const QString &features)
{
m_selectedFont.clearFeatures();
if (features.isEmpty()) {
return;
}
const QStringList rawFeaturesList = features.split(QLatin1Char(','), Qt::SkipEmptyParts);
for (const QString &feature : rawFeaturesList) {
auto f = QStringView(feature).trimmed();
if (f.isEmpty()) {
continue;
}
QList<QStringView> parts = f.split(QStringLiteral("="), Qt::SkipEmptyParts);
if (parts.length() == 2) {
const auto tag = QFont::Tag::fromString(parts[0]);
bool ok = false;
const int number = parts[1].toInt(&ok);
if (tag.has_value() && ok) {
m_selectedFont.setFeature(tag.value(), number);
}
} else if (f.size() <= 4) {
const auto tag = QFont::Tag::fromString(feature);
if (tag.has_value()) {
m_selectedFont.setFeature(tag.value(), 1);
}
}
}
Q_EMIT q->fontSelected(m_selectedFont);
}
我注意到,拆分后的结果已经被去掉了首尾空白:
auto f = QStringView(feature).trimmed();
但原始、未去空白的字符串却仍然被用作 tag:
const auto tag = QFont::Tag::fromString(feature);
这看起来是开发者写错了。要修复这个问题,我们只需要把未去空白的字符串改成去空白后的字符串:
const auto tag = QFont::Tag::fromString(f);
接下来,就该创建一个分支,把改动提交到该分支,并提交合并请求了。不过,由于我对 Git 的经验有限,我遇到了一个问题:
warning: redirecting to https://invent.kde.org/rebellfs/kwidgetsaddons.git/
Enumerating objects: 21973, done.
Counting objects: 100% (21973/21973), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2790/2790), done.
Writing objects: 100% (21973/21973), 41.17 MiB | ... KiB/s, done.
Total 21973 (delta 13428), reused 21374 (delta 12884), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (13428/13428), done.
remote: Audit failure - Commit 046e4bb16e60b66070ee4a00c368ca6924db3a08 - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit 046e4bb16e60b66070ee4a00c368ca6924db3a08 - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit c8c7ab3d0e7ca825bbb01d58de005d4d589b12cc - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit c8c7ab3d0e7ca825bbb01d58de005d4d589b12cc - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit 62e5736a1f4afc2d24ef2c4d643a98bc60e6d6cd - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit 62e5736a1f4afc2d24ef2c4d643a98bc60e6d6cd - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit f317243c63620acec15a46c412502e73b9a04e2a - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Audit failure - Commit f317243c63620acec15a46c412502e73b9a04e2a - Email address has an invalid domain : [kde@randomguy3.me.uk](mailto:kde@randomguy3.me.uk)
remote: Push declined - commits failed audit
remote: Should the audit failures above mention issues regarding your name, please ensure that your Git username has been set to your full name.
remote: Please see https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup for more details on ensuring Git has been fully configured.
remote: In the event that your full name has been set and is shown above as being rejected, please file a Sysadmin ticket at https://go.kde.org/systickets
To https://invent.kde.org/rebellfs/kwidgetsaddons
! [remote rejected] work/rebel1725/fix523757 -> work/rebel1725/fix523757 (pre-receive hook declined)
error: failed to push some refs to 'https://invent.kde.org/rebellfs/kwidgetsaddons'
最后,因为这只是对单个文件的一个小改动,我还是通过浏览器提交了这项修改。
结论
今天,我完成了自己对自由软件社区的第一次贡献,也成为了一名全新的自由软件贡献者。今后,我希望能够贡献更多,不断磨练自己的技能,并且对社区和自由软件运动变得更加有帮助。
















































