Hi,

I'm not 100% sure if my analysis is correct. I therefore write to you before 
opening an issue on GitHub.

If two threads are calling the method 
DatePatternConverter.formatWithoutThreadLocals(Instant,StringBuilder) at the 
same time, one of the threads may incorrectly use the formatted date of the 
other thread in the output. In other words, the event of one thread could be 
written to a log file with the date of the other thread's event.

The following sequence shows how this could happen. All line numbers are based 
on this source code:
https://github.com/apache/logging-log4j2/blob/98c3ceb655d485b0adb48e05d84c8269716f59e8/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java#L327-L338

0. cachedTime contains time t0.

Thread 1:
1. Line 327: Calls the method formatWithoutThreadLocals(instant,output) with 
instant = t1, where t1 > t0.
2. Line 328: Reads t0 from cachedTime into local variable cached.
3. Line 329: if condition evaluates to true (t1 != t0).
4. Line 330: Creates a new CachedTime(t1) and stores it in local variable 
newTime.

Thread 2:
5. Line 327: Calls the method formatWithoutThreadLocals(instant,output) with 
instant = t2, where t2 > t1.
6. Line 328: Reads t0 from cachedTime into local variable cached.
7. Line 329: if condition evaluates to true (t2 != t0).
8. Line 330: Creates a new CachedTime(t2) and stores it in local variable 
newTime.

Thread 1:
9. Line 331: Calls cachedTime.compareAndSet(cached, newTime): This will set the 
shared cachedTime to thread 1's newTime t1 and return true 
(t0.compareAndSet(t0,t1) == true).
10. Line 332: Sets local variable cached = newTime (t1).
11. Line 337: Appends cached.formatted (t1) to output.

Thread 2:
12. Line 331: Calls cachedTime.compareAndSet(cached, newTime): This will NOT 
change the shared cachedTime and return false (t1.compareAndSet(t0,t2) == 
false).
13. Line 334: Sets local variable cached = cachedTime.get(). <-- This will read 
t1 into local variable cached!
14. Line 337: Appends cached.formatted (t1!) to output.

Result: The event recorded by thread 2 at time t2 will be logged with a 
formatted date based on t1, the time when thread 1 has recorded its own event. 
In other words, both events would be written to the log file with the same date 
and time, and the time of event 2 is completely lost. Also, in the presence of 
multiple appenders, the same event can be written to different log files with a 
different date and time.

I think the problem is found at line 334 where thread 2 is reading the time 
that was previously written to cachedTime by thread 1. In order to use the 
correct time, thread 2 should instead set cached = newTime. This means that the 
if block (line 330) and else block (line 332) would be identical. So the return 
value of cachedTime.compareAndSet(cached, newTime) would not be needed, and the 
code could be simplified a lot.

Is my analysis correct, or am I missing something?

Best regards,
Stephan

PS 1: This issue has been found when using Log4j in a web application. This 
means that the use of thread-locals for "gc-free logging" is disabled by 
default.
PS 2: The web application currently uses only synchronous loggers and appenders.
Email Disclaimer
FNZ (UK) Ltd registered in England and Wales (05435760) 10th Floor, 135 
Bishopsgate, London EC2M 3TP, FNZ (UK) Ltd is authorised and regulated by the 
Financial Conduct Authority (438687); FNZ TA Services Ltd registered in England 
and Wales (09571767) 10th Floor, 135 Bishopsgate, London EC2M 3TP, FNZ TA 
Services Ltd is authorised and regulated by the Financial Conduct Authority 
(932253); FNZ Securities Ltd registered in England and Wales (09486463) 10th 
Floor, 135 Bishopsgate, London EC2M 3TP, FNZ Securities Ltd, is authorised and 
regulated by the Financial Conduct Authority (733400); JHC Systems Limited 
registered in England and Wales (08729370) Temple Point 6th Floor, 1 Temple 
Row, Birmingham, West Midlands, B2 5LG; FNZ (Europe) DAC registered in Ireland 
(657886)  Block C, Irish Life Centre, Lower Abbey Street, Dublin 1, D01V9F5, 
Ireland; FNZ SA (Pty) Ltd registered under the laws of South Africa 
(2018/547997/07), 1st floor, Newport House, Prestwich Street, Greenpoint, 
western Cape, 8001; FNZ Limited registered in New Zealand (1797706) FNZ House, 
Level 3, 29A Brandon Street, Wellington, 6011 New Zealand; FNZ (Australia) Pty 
Ltd registered in Australia (138 819 119) Level 1, 99 Elizabeth St, Sydney 
2000; FNZ (Hong Kong) Limited registered in Hong Kong (1305362) 6A-1, Koshun 
House, 331 Nathan Road, Hong Kong; FNZ (Singapore) Services Pte. Ltd. 
registered in Singapore (201307199R) 61 Robinson Road, #13-03A, Robinson 
Centre, Singapore (068893); and FNZ (China) Ltd registered in China 
(91310115MA1K3G4K6T) [中国(上海)自由贸易试验区世纪大道1196 号二 座20 层.
This message is intended solely for the addressee and may contain confidential 
information. If you have received this message in error, please send it back to 
us, and immediately and permanently delete it. Do not use, copy or disclose the 
information contained in this message or in any attachment. 
Emails sent to and from FNZ may be monitored and read for legitimate business 
purposes. Emails cannot be guaranteed to be secure or error-free, and you 
should protect your systems. FNZ does not accept any liability arising from 
interception, corruption, loss or destruction of this email, or if it arrives 
late or incomplete or with errors or viruses.
For more information about the FNZ group please visit our website here where 
you can also find links to our policies including our Privacy policy which 
provides information about what we do with your personal data.

Reply via email to