Repository navigation
Stop Poisoning Bytebuddy #726
Description
incision: ManualSelfAttach permanently sets jdk.attach.allowAttachSelf=true, breaking agent attachment for other plugins (e.g. ByteBuddy users)
Body:
Summary
taboolib.module.incision.loader.attach.ManualSelfAttach.attach() unconditionally executes:
// 启用 self-attach(JDK 9+)
System.setProperty("jdk.attach.allowAttachSelf", "true")taboolib/module/incision/src/main/kotlin/taboolib/module/incision/loader/attach/ManualSelfAttach.kt
Line 37 in b848958
| System.setProperty("jdk.attach.allowAttachSelf", "true") |
This property write is process-global and is never reverted. It cannot achieve what the comment intends, and it silently breaks Java agent attachment for every other plugin in the same JVM that uses ByteBuddy (or any library that consults this property at runtime).
Why setting the property at runtime cannot work
HotSpot snapshots this property at JVM startup. sun.tools.attach.HotSpotVirtualMachine reads it via VM.getSavedProperty, not System.getProperty:
static {
String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);
}
...
if (!ALLOW_ATTACH_SELF && selfAttach) {
throw new IOException("Can not attach to current VM");
}So unless -Djdk.attach.allowAttachSelf=true was passed on the command line, incision's own self-attach fails regardless of the setProperty call. Incision then falls back to its ASM-only path — but the poisoned property remains set for the lifetime of the process.
Why this breaks other plugins
net.bytebuddy.agent.ByteBuddyAgent chooses its attachment strategy from the live system property (ByteBuddyAgent$AttachmentTypeEvaluator$InstallationAction):
if (Boolean.getBoolean("jdk.attach.allowAttachSelf")) {
return Disabled.INSTANCE; // direct in-process self-attach
}Normally (property unset), ByteBuddy detects a self-attach and transparently spawns an external helper process to perform the attach, which works fine without any JVM flags. After incision has set the property, ByteBuddy believes in-process self-attach is permitted, skips the external fallback, calls VirtualMachine.attach(<own pid>) directly — and the JDK rejects it with IOException: Can not attach to current VM, because the startup snapshot still says self-attach is disallowed.
Net effect: the JDK ignores the runtime value, ByteBuddy trusts it, and the setProperty call drives the two apart. Any plugin loading after a TabooLib plugin with the incision module can no longer install a Java agent.
TrChat 2.4.10 (TabooLib 6.3.0, incision installed) + Iris on Paper: Iris self-attaches its instrumentation agent via ByteBuddyAgent.attach(...) at startup. Without TrChat this works. With TrChat installed, Iris's attach fails with Can not attach to current VM and it reports its safeguard error, even though the server was started with -XX:+EnableDynamicAgentLoading. Removing TrChat (or adding -Djdk.attach.allowAttachSelf=true so the runtime property matches reality) resolves it.
PLEASE:
Remove the System.setProperty call entirely — it cannot enable self-attach on HotSpot. If there is a VM where it does help, at minimum snapshot the previous value and restore it (including clearing it if it was unset) when the attach attempt fails, so the failed experiment doesn't leak process-global state into unrelated plugins.
You can see my plugin here:
https://github.com/VolmitSoftware/Iris
Thank you
TRANSLATED:
标题:
incision: ManualSelfAttach 永久设置 jdk.attach.allowAttachSelf=true,破坏其他插件的 agent 附加(例如 ByteBuddy 用户)
正文:
摘要
taboolib.module.incision.loader.attach.ManualSelfAttach.attach() 无条件执行:
// 启用 self-attach(JDK 9+)
System.setProperty("jdk.attach.allowAttachSelf", "true")taboolib/module/incision/src/main/kotlin/taboolib/module/incision/loader/attach/ManualSelfAttach.kt
Line 37 in b848958
| System.setProperty("jdk.attach.allowAttachSelf", "true") |
此属性写入是进程全局的,且从未还原。它无法实现注释中的意图,并会静默破坏同一 JVM 中所有使用 ByteBuddy(或任何在运行时检查此属性的库)的其他插件的 Java agent 附加。
为什么在运行时设置该属性无效
HotSpot 在 JVM 启动时快照此属性。sun.tools.attach.HotSpotVirtualMachine 通过 VM.getSavedProperty 读取它,而不是 System.getProperty:
static {
String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);
}
...
if (!ALLOW_ATTACH_SELF && selfAttach) {
throw new IOException("Can not attach to current VM");
}因此,除非启动时传入 -Djdk.attach.allowAttachSelf=true,否则 incision 自身的 self-attach 无论是否调用 setProperty 都会失败。Incision 随后回退到纯 ASM 路径,但被污染的属性会在进程生命周期内一直保留。
为什么这会破坏其他插件
net.bytebuddy.agent.ByteBuddyAgent 根据实时系统属性选择附加策略(ByteBuddyAgent$AttachmentTypeEvaluator$InstallationAction):
if (Boolean.getBoolean("jdk.attach.allowAttachSelf")) {
return Disabled.INSTANCE; // 直接进程内 self-attach
}正常情况下(属性未设置),ByteBuddy 检测到 self-attach 后会透明地启动一个外部辅助进程来执行附加,无需任何 JVM 参数即可正常工作。当 incision 设置了该属性后,ByteBuddy 认为进程内 self-attach 已允许,跳过外部回退,直接调用 VirtualMachine.attach(<own pid>)——而 JDK 会因启动快照仍禁止 self-attach 而抛出 IOException: Can not attach to current VM。
最终结果:JDK 忽略运行时值,ByteBuddy 信任它,setProperty 调用使两者产生分歧。任何在带有 incision 模块的 TabooLib 插件之后加载的插件都无法再安装 Java agent。
TrChat 2.4.10(TabooLib 6.3.0,安装了 incision)+ Iris on Paper:Iris 在启动时通过 ByteBuddyAgent.attach(...) 自附加其 instrumentation agent。没有 TrChat 时正常。安装 TrChat 后,Iris 的附加失败并报 Can not attach to current VM,即使服务器启动时使用了 -XX:+EnableDynamicAgentLoading。移除 TrChat(或添加 -Djdk.attach.allowAttachSelf=true 使运行时属性与实际一致)即可解决。
请求:
请完全移除 System.setProperty 调用——它无法在 HotSpot 上启用 self-attach。如果有某些 VM 确实有效,至少应快照原值,并在附加失败时恢复(包括原未设置时清除),避免失败的实验泄漏进程全局状态到无关插件。
我的插件在此:
https://github.com/VolmitSoftware/Iris
谢谢
Activity
FxRayHughes commentedon Aug 8, 2026
感谢报告。我们已独立验证了这个问题,并已在 PR #725 的提交
818f2169中修复。调查结果:
ALLOW_ATTACH_SELF在 JVM 启动时通过VM.getSavedProperty快照,运行时System.setProperty无法真正开启 self-attach。System.getProperty,因此 Incision 的写入会让 ByteBuddy 误判为允许进程内 attach,跳过外部 helper,进而影响 Iris 等其他插件。GetEnv、ClassFileLoadHook或RetransformClasses路径。修复内容:
ManualSelfAttach的运行时属性写入。-Djdk.attach.allowAttachSelf=true时才使用进程内 attach。我们没有采用“失败后恢复属性”方案,因为并发插件仍可能在恢复前读取到错误值,而且无法区分用户或其他插件原本设置的值。
FxRayHughes commentedon Aug 8, 2026
更新:由于 PR #725 已在本次提交前合并,属性修复已单独提交到新的 PR #727:
#727
PR #727 只包含提交
818f2169,并关联本 Issue。NextdoorPsycho commentedon Aug 8, 2026
感谢你们快速修复了 #726 的问题!
PR #727 已经合并,解决了我遇到的 ByteBuddy 冲突。非常感谢!
FxRayHughes commentedon Aug 8, 2026
因为这个功能不是最核心的,我们最核心的功能还是 JVMTI native 模式。 本来想作为向下兼容的方案没想到成为了累赘