From 880b574376ff24ca6c6df6b681fd69a3956d7486 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Thu, 2 Jul 2026 20:35:25 +0200 Subject: [PATCH] [jsscripting] Fix illegal multi-threaded access when using JSRule (#21093) When using JSRule, the created SimpleRule is wrapper by ThreadsafeSimpleRuleDelegate to add the necessary synchronization. If lock acquisition failed within the configured time or was interrupted, it added the rule UID to the message, calling `delegate#getUID`, which is overwritten from JS and thereby causes a GraalJS context access. Signed-off-by: Florian Hotze --- .../threading/ThreadsafeSimpleRuleDelegate.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/threading/ThreadsafeSimpleRuleDelegate.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/threading/ThreadsafeSimpleRuleDelegate.java index d9f338e8c8..451dc0b9ec 100644 --- a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/threading/ThreadsafeSimpleRuleDelegate.java +++ b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/threading/ThreadsafeSimpleRuleDelegate.java @@ -43,6 +43,11 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler { private final Lock lock; private final long lockAcquisitionTimeoutMS; private final SimpleRule delegate; + /** + * Get and store the UID upon wrapping as {@link SimpleRule#getUID()} is overwritten in openhab-js, + * which means calling it is a GraalJS context access and needs to be synchronized. + */ + private final String uid; /** * Constructor requires a lock object and delegate to forward invocations to. @@ -55,6 +60,7 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler { this.lock = lock; this.lockAcquisitionTimeoutMS = lockAcquisitionTimeoutMS; this.delegate = delegate; + this.uid = delegate.getUID(); } @Override @@ -66,7 +72,7 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler { } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Interrupted while waiting to acquire the lock for action '" + module.getId() - + "' of rule '" + delegate.getUID() + '\'', e); + + "' of rule '" + uid + '\'', e); } if (locked) { try { @@ -76,15 +82,14 @@ class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler { lock.unlock(); } } else { - throw new RuntimeException( - "Failed to acquire the lock for action '" + module.getId() + "' of rule '" + delegate.getUID() - + "' within " + TimeUnit.MILLISECONDS.toSeconds(lockAcquisitionTimeoutMS) + " seconds."); + throw new RuntimeException("Failed to acquire the lock for action '" + module.getId() + "' of rule '" + uid + + "' within " + TimeUnit.MILLISECONDS.toSeconds(lockAcquisitionTimeoutMS) + " seconds."); } } @Override public String getUID() { - return delegate.getUID(); + return uid; } @Override